godotengine / godotengine/godot
Float-to-int cast and rounding math functions are inconsistent between platforms on non-finite cases
- Dominant language
- C++
- Stars
- 117k
- Forks
- 26.8k
- PR merge metrics
- PR metrics pending
Description
### Tested versions
- Reproducible in: v4.3.stable.flathub [77dcf97d8]
### System information
Phone:
Android 10, Samsung Galaxy J6+ (SM-J610G), armeabi-v7a - OpenGL API OpenGL ES 3.0 V@415.0 (GIT@d39f783, I79de86aa2c, 1591296226) (Date:06/04/20) - Compatibility - Using Device: Qualcomm - Adreno (TM) 308
PC (x64):
Godot v4.3.stable (77dcf97d8) - Freedesktop SDK 24.08 (Flatpak runtime) - X11 - GLES3 (Compatibility) - Mesa Intel(R) HD Graphics 5500 (BDW GT2) - Intel(R) Core(TM) i5-5300U CPU @ 2.30GHz (4 Threads)
### Issue description
The GDScript math functions that round/cast a float to int (i.e. `ceili`, `floori`, `roundi`, `snappedi`, and `int` cast/constructor) are inconsistent between platforms on the edge cases of `(-/+)INF` and `NAN`. I'm going to guess this causes a bunch of other very hard-to-detect issues (like it did for me).
On Android it's not returning the same as on Web and in Linux (the editor).
I haven't tested other versions or other platforms.
I'm using GDScript, not the .NET-enabled version of Godot.
If you're providing these functions in core/GDScript, then I would expect at least consistency between platforms.
Android is the weird one here, since others seem to treat them as the `-9223372036854775808` negative limit value.
Then again ... even .NET itself seems to handle these differently by versions on my tests:
`csharp` command (Mono / .NET Framework 4) casts NaN,Infinity to `-9223372036854775808`
`dotnet-script` command (.NET 8) casts NaN,Infinity to `0`
No idea how e.g. C++ etc. handle these, or if it can be platform/architecture/language-dependent.
### Steps to reproduce
```gdscript
func _enter_tree() -> void:
prints(int(-INF), int(INF), int(NAN)) # int cast is equivalent to a truncatei operation
prints(ceili(-INF), ceili(INF), ceili(NAN))
prints(floori(-INF), floori(INF), floori(NAN))
prints(roundi(-INF), roundi(INF), roundi(NAN))
prints(snappedi(-INF, 0.5), snappedi(INF, 0.5), snappedi(NAN, 0.5))
```
All of these print:
- on Linux editor and Web: `-9223372036854775808 -9223372036854775808 -9223372036854775808`
- on the Android device: `-1 1 0`
IMHO, neither of the results above seem right (though Android seems very wrong).
I would expect this:
- `INF` input: treated as `0x7FFFFFFFFFFFFFFF` = `9223372036854775807` (max value)
- `-INF` input: treated as `0x8000000000000001` = `-9223372036854775807` (negative of max value);
either that or `0x8000000000000000` = `-9223372036854775808` (negative limit value)
- `NAN` input: treated as `0x8000000000000000` = `-9223372036854775808` (value with no positive counterpart)
Granted, I guess these aren't extremely reliable ... but this would IMHO be more consistent when making comparisons involving infinite cases. But I'm reporting this mostly because of the Android inconsistency (hidden bug?).
### Minimal reproduction project (MRP)
N/A
Contributor guide
Research direction
Start by running the GDScript reproduction for int, ceili, floori, roundi, and snappedi with -INF, INF, and NAN on the reported platforms. Trace the implementation of these GDScript math functions and compare their non-finite handling across Android, Linux, and Web. Done means the chosen behavior is defined and the functions produce consistent results across platforms.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, cpp, godot
- Domain
- game-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100