-
Notifications
You must be signed in to change notification settings - Fork 883
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unpacking float32 does not preserve signaling NaN, unlike float64 #1087
Comments
Thank you for reporting the issue. Which one has the isssue C or C++, or both? |
I study about signaling/quiet NaN. Now I understand the issue. I consider fixing the issue with minimal code change. This code demonstrates the issue (signaling NaN becomes quiet NaN when convert from float to double), and another possible fix. Your link indicates the issue is caused by the following function. I agree. inline void unpack_float(float d, msgpack::object& o)
{ o.type = msgpack::type::FLOAT; o.via.f64 = d; } When I apply the minimal fix, then the code would be as follows: inline void unpack_float(float d, msgpack::object& o)
{
o.type = msgpack::type::FLOAT;
o.via.f64 = d;
if (isnan(d)) { // could be `if (d != d) { ` for porablility
if (!is_quiet_nan(d)) {
clear_quiet(o.via.f64);
}
}
} What do you think ? Bitimage print version: https://godbolt.org/z/rqaPx4b34 |
I ran my poc code on windows/msvc2019. Here is the result:
Linux result:
float signaling NaN
*1 It seems that only Fraction != 0 is defined. I think that your solution, introducing float type to object union, is better than my approach. However, there is still problem.
similarlly
*2 I'm still not sure how to distinguish quiet or signal. I think that the following code is not portable. Works well on Linux but doesn't work on Windows: inline bool is_quiet_nan(float nan_val) {
std::uint32_t bit_pattern = reinterpret_cast<std::uint32_t&>(nan_val);
int is_quiet_bit_index = FLT_MANT_DIG - 2;
return (bit_pattern >> is_quiet_bit_index) & 1;
} |
Perhaps msgpack-c could have a database of double/float signaling/quiet NaN bit representation on common operating systems, like Linux, macosx, Windows. Conflict means OS-A quiet NaN bit representation is the same as OS-B signaling NaN bit representation. Is there any information about quiet/signaling NaN bit repserentation ? |
I testes on msvc2022 too. I found a document:
It seems that the document and result are not mached. |
Treat signaling NaN correctly.
Treat signaling NaN correctly. Introduced f32 to preserve signaling/quiet NaN. Updated zlib on CI.
Treat signaling NaN correctly. Introduced f32 to preserve signaling/quiet NaN. Updated zlib on CI.
Treat signaling NaN correctly. Introduced f32 to preserve signaling/quiet NaN. Updated zlib on CI.
I wrote a PR to fix this issue on C++. 32bithttps://godbolt.org/z/8ETYd3Gz4
64bithttps://godbolt.org/z/aa8Wxoaao
64 bit is expected behavior. The left most bit of Fraction(Significand) indicates quiet(1) or signaling(0). |
https://en.wikipedia.org/wiki/IEEE_754#2019
Perhaps the encoding rule for signaling/quiet NaN is very recently decided. If it is, there are various kinds of encoding... How do I write test for that ... try rising signal ?? |
I checked signal using fetestexcept. signaling NaN with floating operation results:
|
Thanks for looking into this @redboltz! I think the Windows issue you encountered may just be an issue with the Windows C++ standard library implementation: https://developercommunity.visualstudio.com/t/stdnumeric-limitssignaling-nan-returns-quiet-nan/155064. Perhaps a floating point operation that generates an sNAN still has the correct bit pattern even if Is the 32-bit CPU issue you encountered the same as the above Windows issue? Your solution of clearing the |
Actually, the Windows issue is a 32-bit CPU issue, not the other way around! More details here. From what I understood, if a function returns an sNAN, it will always get converted to a qNAN (and raise a floating point exception) because the calling conventions on x86 specify that floating point values are returned in the x87 registers, which are inside the FPU. Hence, |
Describe the bug
float32/float64 NaNs have a quiet vs. signaling state. This state is encoded using a couple bits in the significand.
Unpacking a float64 signaling NaN using msgpack-c 6.0.0 preserves the quiet vs. signaling state. However, unpacking a float32 signaling NaN always results in a quiet NaN even if the packed value is a signaling NaN. The behavior is inconsistent between float32 and float64.
To Reproduce
Expected behavior
Either both tests should pass or both tests should fail. Currently,
simple_buffer_double_signaling_nan
passes butsimple_buffer_float_signaling_nan
fails.Root cause
The unpacker returns float32 values as
double
. This conversion implicitly causes the float32 signaling NaN to become a float64 quiet NaN.Possible solution
The unpacker could return float32 values as
float
instead of converting them todouble
. This might be a breaking change because callers may need to update their usage to check a differentmsgpack_object_union
field. However, in addition to resolving this issue, it could also be more convenient for callers because they wouldn’t need to cast thedouble
field back tofloat
.The text was updated successfully, but these errors were encountered: