You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
C:\Temp>type repro.cpp
#include<iostream>
#include<cmath>intmain(){
usingscalar_t = float;
scalar_t nextBefOne = std::nextafter<scalar_t>(scalar_t(1), scalar_t(0)); // does not work// scalar_t nextBefOne = std::nextafter(scalar_t(1), scalar_t(0)); // works as expectedif (nextBefOne < scalar_t(1))
std::cout << "Sucess" <<std::endl;
else
std::cout << "Failure" <<std::endl;
}
C:\Temp>cl /EHcs /W4 /std::c++latest .\repro.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.41.34120 for x86
Copyright (C) Microsoft Corporation. All rights reserved.
cl : Command line warning D9002 : ignoring unknown option '/std::c++latest'
repro.cpp
C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.41.34120\include\cmath(822): warning C4244: 'return': conversion from 'double' to 'float', possible loss of data
C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.41.34120\include\cmath(822): note: the template instantiation context (the oldest one first) is
.\repro.cpp(6): note: see reference to function template instantiation 'float nextafter<main::scalar_t,main::scalar_t,0>(_Ty1,_Ty2) noexcept' being compiled
with
[
_Ty1=main::scalar_t,
_Ty2=main::scalar_t
]
Microsoft (R) Incremental Linker Version 14.41.34120.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:repro.exe
repro.obj
C:\Temp>.\repro.exe
Failure
Expected behavior
std::nextafter should return the next representable value for both single precision floats and double precision floats independent whether the type was specified. However since msvc v19.36 VS17.6 it has stopped working for single precision floats. For doubles it works as expected.
The text was updated successfully, but these errors were encountered:
Thanks for reporting this issue. We're resolving it as By Design.
The proximate cause was merging #3253 in VS 2022 17.6, which altered how we provide <cmath>'s (notoriously complicated) "sufficient additional overloads".
According to the Standard, <cmath> doesn't necessarily provide function templates - it simply ensures (through some combination of overloads and/or templates) that calls to the math functions with various arithmetic arguments produce the specified results. Giving explicit template arguments to a <cmath> function is non-Standard, because they aren't guaranteed to be templates in the first place. This is WG21-N4988 [cmath.syn]/2-3:
For each function with at least one parameter of type floating-point-type, the implementation provides an overload for each cv-unqualified floating-point type (6.8.2) where all uses of floating-point-type in the function signature are replaced with that floating-point type.
For each function with at least one parameter of type floating-point-type other than abs, the implementation also provides additional overloads sufficient to ensure that, if every argument corresponding to a floating-point-type parameter has arithmetic type, then every such argument is effectively cast to the floating-point type with the greatest floating-point conversion rank and greatest floating-point conversion subrank among the types of all such arguments, where arguments of integer type are considered to have the same floating-point conversion rank as double. If no such floating-point type with the greatest rank and subrank exists, then overload resolution does not result in a usable candidate (12.2.1) from the overloads provided by the implementation.
The root cause of what's physically happening is that we now provide a plain (float, float) overload that absorbs those arguments. We then provide a template to handle all other combos of double, long double, and integers, which performs its work as double. The problem with passing an explicit template argument of float is that it disrupts the overload resolution that we expect (from Standard-conforming user code), bypassing the plain (float, float) overload.
The solution, as you've found, is to simply call std::nextafter without explicit template arguments. If you want to modify the inputs, convert the arguments before the call.
(I gave a talk about this principle, Don't Help The Compiler. The idea is to avoid passing explicit template arguments to functions that weren't intentionally designed for it - e.g. make_shared<T> is of course correct, but swap, make_pair, and in this case nextafter should always be called via template argument deduction.)
Test case
Compare also the difference to versions before v19.36 VS17.6 in https://godbolt.org/z/zzqxKoaqK
Expected behavior
std::nextafter should return the next representable value for both single precision floats and double precision floats independent whether the type was specified. However since msvc v19.36 VS17.6 it has stopped working for single precision floats. For doubles it works as expected.
The text was updated successfully, but these errors were encountered: