Bug 23846 – std.math can't compile under macos rosetta
Status
RESOLVED
Resolution
FIXED
Severity
major
Priority
P1
Component
phobos
Product
D
Version
D2
Platform
Other
OS
Mac OS X
Creation time
2023-04-19T15:18:44Z
Last change time
2023-05-09T05:59:35Z
Assigned to
No Owner
Creator
John Colvin
Comments
Comment #0 by john.loughran.colvin — 2023-04-19T15:18:44Z
This is on an M2 Pro chip, running dmd x64 under rosetta (which is the default you get if you run `install.sh`)
(dmd-2.103.0)john@Johns-MacBook-Pro ~ % cat test.d
import std.math;
(dmd-2.103.0)john@Johns-MacBook-Pro ~ % dmd test.d
/Users/john/dlang/dmd-2.103.0/osx/bin/../../src/phobos/std/math/exponential.d(3791): Error: number `0x0.8p-126f` is not representable as a `float`
/Users/john/dlang/dmd-2.103.0/osx/bin/../../src/phobos/std/math/exponential.d(3791): https://dlang.org/spec/lex.html#floatliteral
/Users/john/dlang/dmd-2.103.0/osx/bin/../../src/phobos/std/math/exponential.d(3791): Error: number `0x0.8p-126f` is not representable as a `float`
/Users/john/dlang/dmd-2.103.0/osx/bin/../../src/phobos/std/math/exponential.d(3791): https://dlang.org/spec/lex.html#floatliteral
/Users/john/dlang/dmd-2.103.0/osx/bin/../../src/phobos/std/math/exponential.d(3793): Error: number `0x0.555556p-126f` is not representable as a `float`
/Users/john/dlang/dmd-2.103.0/osx/bin/../../src/phobos/std/math/exponential.d(3793): https://dlang.org/spec/lex.html#floatliteral
/Users/john/dlang/dmd-2.103.0/osx/bin/../../src/phobos/std/math/exponential.d(3793): Error: number `0x0.555556p-126f` is not representable as a `float`
/Users/john/dlang/dmd-2.103.0/osx/bin/../../src/phobos/std/math/exponential.d(3793): https://dlang.org/spec/lex.html#floatliteral
/Users/john/dlang/dmd-2.103.0/osx/bin/../../src/phobos/std/math/exponential.d(3804): Error: number `0x0.8p-1022` is not representable as a `double`
/Users/john/dlang/dmd-2.103.0/osx/bin/../../src/phobos/std/math/exponential.d(3804): `real` literals can be written using the `L` suffix. https://dlang.org/spec/lex.html#floatliteral
/Users/john/dlang/dmd-2.103.0/osx/bin/../../src/phobos/std/math/exponential.d(3804): Error: number `0x0.8p-1022` is not representable as a `double`
/Users/john/dlang/dmd-2.103.0/osx/bin/../../src/phobos/std/math/exponential.d(3804): `real` literals can be written using the `L` suffix. https://dlang.org/spec/lex.html#floatliteral
/Users/john/dlang/dmd-2.103.0/osx/bin/../../src/phobos/std/math/exponential.d(3806): Error: number `0x0.5555555555555p-1022` is not representable as a `double`
/Users/john/dlang/dmd-2.103.0/osx/bin/../../src/phobos/std/math/exponential.d(3806): `real` literals can be written using the `L` suffix. https://dlang.org/spec/lex.html#floatliteral
/Users/john/dlang/dmd-2.103.0/osx/bin/../../src/phobos/std/math/exponential.d(3806): Error: number `0x0.5555555555555p-1022` is not representable as a `double`
/Users/john/dlang/dmd-2.103.0/osx/bin/../../src/phobos/std/math/exponential.d(3806): `real` literals can be written using the `L` suffix. https://dlang.org/spec/lex.html#floatliteral
Comment #1 by calebcenter — 2023-04-22T17:14:36Z
I am seeing a similar/same error in building dmd x64 on Intel-based macOS when trying to re-build DMD 2.103.0 as part of Homebrew packaging. So I think Rosetta can be ruled out as the culprit here.
We were able to build DMD 2.103.0 when it was initially released (~3 weeks ago) but it looks like it doesn't build anymore. My initial guess is that this may be related to Xcode 14.3 which was released recently and has been updated in Homebrew CI machines.
The following error is encountered when trying to build phobos:
/private/tmp/dmd-20230422-6518-pzw42y/dmd-2.103.0/generated/osx/release/64/dmd -conf= -I/private/tmp/dmd-20230422-6518-pzw42y/dmd-2.103.0/druntime/import -w -de -preview=dip1000 -preview=dtorfields -preview=fieldwise -m64 -fPIC -O -release -lib -ofgenerated/osx/release/64/libphobos2.a /private/tmp/dmd-20230422-6518-pzw42y/dmd-2.103.0/druntime/../generated/osx/release/64/libdruntime.a std/array.d std/ascii.d std/base64.d std/bigint.d std/bitmanip.d std/checkedint.d std/compiler.d std/complex.d std/concurrency.d std/conv.d std/csv.d std/demangle.d std/encoding.d std/exception.d std/file.d std/functional.d std/getopt.d std/int128.d std/json.d std/mathspecial.d std/meta.d std/mmfile.d std/numeric.d std/outbuffer.d std/package.d std/parallelism.d std/path.d std/process.d std/random.d std/signals.d std/socket.d std/stdint.d std/stdio.d std/string.d std/sumtype.d std/system.d std/traits.d std/typecons.d std/uri.d std/utf.d std/uuid.d std/variant.d std/zip.d std/zlib.d std/algorithm/comparison.d std/algorithm/iteration.d std/algorithm/mutation.d std/algorithm/package.d std/algorithm/searching.d std/algorithm/setops.d std/algorithm/sorting.d std/container/array.d std/container/binaryheap.d std/container/dlist.d std/container/package.d std/container/rbtree.d std/container/slist.d std/container/util.d std/datetime/date.d std/datetime/interval.d std/datetime/package.d std/datetime/stopwatch.d std/datetime/systime.d std/datetime/timezone.d std/digest/crc.d std/digest/hmac.d std/digest/md.d std/digest/murmurhash.d std/digest/package.d std/digest/ripemd.d std/digest/sha.d std/experimental/allocator/common.d std/experimental/allocator/gc_allocator.d std/experimental/allocator/mallocator.d std/experimental/allocator/mmap_allocator.d std/experimental/allocator/package.d std/experimental/allocator/showcase.d std/experimental/allocator/typed.d std/experimental/allocator/building_blocks/affix_allocator.d std/experimental/allocator/building_blocks/aligned_block_list.d std/experimental/allocator/building_blocks/allocator_list.d std/experimental/allocator/building_blocks/ascending_page_allocator.d std/experimental/allocator/building_blocks/bucketizer.d std/experimental/allocator/building_blocks/fallback_allocator.d std/experimental/allocator/building_blocks/free_list.d std/experimental/allocator/building_blocks/free_tree.d std/experimental/allocator/building_blocks/bitmapped_block.d std/experimental/allocator/building_blocks/kernighan_ritchie.d std/experimental/allocator/building_blocks/null_allocator.d std/experimental/allocator/building_blocks/package.d std/experimental/allocator/building_blocks/quantizer.d std/experimental/allocator/building_blocks/region.d std/experimental/allocator/building_blocks/scoped_allocator.d std/experimental/allocator/building_blocks/segregator.d std/experimental/allocator/building_blocks/stats_collector.d std/experimental/logger/core.d std/experimental/logger/filelogger.d std/experimental/logger/nulllogger.d std/experimental/logger/multilogger.d std/experimental/logger/package.d std/format/package.d std/format/read.d std/format/spec.d std/format/write.d std/format/internal/floats.d std/format/internal/read.d std/format/internal/write.d std/logger/core.d std/logger/filelogger.d std/logger/nulllogger.d std/logger/multilogger.d std/logger/package.d std/math/algebraic.d std/math/constants.d std/math/exponential.d std/math/hardware.d std/math/operations.d std/math/package.d std/math/remainder.d std/math/rounding.d std/math/traits.d std/math/trigonometry.d std/net/curl.d std/net/isemail.d std/uni/package.d std/experimental/checkedint.d std/range/interfaces.d std/range/package.d std/range/primitives.d std/regex/package.d std/regex/internal/generator.d std/regex/internal/ir.d std/regex/internal/parser.d std/regex/internal/backtracking.d std/regex/internal/tests.d std/regex/internal/tests2.d std/regex/internal/thompson.d std/regex/internal/kickstart.d std/windows/charset.d std/windows/registry.d std/windows/syserror.d etc/c/curl.d etc/c/odbc/sql.d etc/c/odbc/sqlext.d etc/c/odbc/sqltypes.d etc/c/odbc/sqlucode.d etc/c/sqlite3.d etc/c/zlib.d std/algorithm/internal.d std/internal/cstring.d std/internal/memory.d std/internal/digest/sha_SSSE3.d std/internal/math/biguintcore.d std/internal/math/biguintnoasm.d std/internal/math/biguintx86.d std/internal/math/errorfunction.d std/internal/math/gammafunction.d std/internal/scopebuffer.d std/internal/test/dummyrange.d std/internal/test/range.d std/internal/unicode_comp.d std/internal/unicode_decomp.d std/internal/unicode_grapheme.d std/internal/unicode_norm.d std/internal/unicode_tables.d std/internal/windows/advapi32.d std/typetuple.d generated/osx/release/64/etc/c/zlib/adler32.o generated/osx/release/64/etc/c/zlib/compress.o generated/osx/release/64/etc/c/zlib/crc32.o generated/osx/release/64/etc/c/zlib/deflate.o generated/osx/release/64/etc/c/zlib/gzclose.o generated/osx/release/64/etc/c/zlib/gzlib.o generated/osx/release/64/etc/c/zlib/gzread.o generated/osx/release/64/etc/c/zlib/gzwrite.o generated/osx/release/64/etc/c/zlib/infback.o generated/osx/release/64/etc/c/zlib/inffast.o generated/osx/release/64/etc/c/zlib/inflate.o generated/osx/release/64/etc/c/zlib/inftrees.o generated/osx/release/64/etc/c/zlib/trees.o generated/osx/release/64/etc/c/zlib/uncompr.o generated/osx/release/64/etc/c/zlib/zutil.o
std/math/exponential.d(3791): Error: number `0x0.8p-126f` is not representable as a `float`
std/math/exponential.d(3791): https://dlang.org/spec/lex.html#floatliteral
std/math/exponential.d(3791): Error: number `0x0.8p-126f` is not representable as a `float`
std/math/exponential.d(3791): https://dlang.org/spec/lex.html#floatliteral
std/math/exponential.d(3793): Error: number `0x0.555556p-126f` is not representable as a `float`
std/math/exponential.d(3793): https://dlang.org/spec/lex.html#floatliteral
std/math/exponential.d(3793): Error: number `0x0.555556p-126f` is not representable as a `float`
std/math/exponential.d(3793): https://dlang.org/spec/lex.html#floatliteral
std/math/exponential.d(3804): Error: number `0x0.8p-1022` is not representable as a `double`
std/math/exponential.d(3804): `real` literals can be written using the `L` suffix. https://dlang.org/spec/lex.html#floatliteral
std/math/exponential.d(3804): Error: number `0x0.8p-1022` is not representable as a `double`
std/math/exponential.d(3804): `real` literals can be written using the `L` suffix. https://dlang.org/spec/lex.html#floatliteral
std/math/exponential.d(3806): Error: number `0x0.5555555555555p-1022` is not representable as a `double`
std/math/exponential.d(3806): `real` literals can be written using the `L` suffix. https://dlang.org/spec/lex.html#floatliteral
std/math/exponential.d(3806): Error: number `0x0.5555555555555p-1022` is not representable as a `double`
std/math/exponential.d(3806): `real` literals can be written using the `L` suffix. https://dlang.org/spec/lex.html#floatliteral
make: *** [generated/osx/release/64/libphobos2.a] Error 1
Comment #2 by kinke — 2023-04-22T18:57:39Z
(In reply to Caleb Xu from comment #1)
> We were able to build DMD 2.103.0 when it was initially released (~3 weeks
> ago) but it looks like it doesn't build anymore. My initial guess is that
> this may be related to Xcode 14.3 which was released recently and has been
> updated in Homebrew CI machines.
Thanks for this insight. DMD uses the C runtime's `strto{f,d}` to check for over/underflows of float/double literals (in `Port.isFloat{32,64}LiteralOutOfRange()`). So an Apple libc change might indeed be the culprit. - LDC isn't affected, it uses LLVM functionality to parse floating-point literals.
Comment #3 by calebcenter — 2023-04-23T11:55:40Z
(In reply to kinke from comment #2)
> Thanks for this insight. DMD uses the C runtime's `strto{f,d}` to check for
> over/underflows of float/double literals (in
> `Port.isFloat{32,64}LiteralOutOfRange()`). So an Apple libc change might
> indeed be the culprit. - LDC isn't affected, it uses LLVM functionality to
> parse floating-point literals.
Thanks for the tip. I tried this simple C program (based on strto{d,f} invocations from [1], [2]) on a machine with the new Xcode and it outputs four "true"s as expected. So far this seems to work, at least as far as not throwing an error when parsing the strings into float/double:
#include <stdio.h>
#include <stdlib.h>
int main() {
float float1 = strtof("0x0.8p-126f", NULL);
printf("%s\n", (float1 != 0) ? "true" : "false");
float float2 = strtof("0x0.555556p-126f", NULL);
printf("%s\n", (float2 != 0) ? "true" : "false");
double double1 = strtod("0x0.8p-1022", NULL);
printf("%s\n", (double1 != 0) ? "true" : "false");
double double2 = strtod("0x0.5555555555555p-1022", NULL);
printf("%s\n", (double2 != 0) ? "true" : "false");
}
[1]: https://github.com/dlang/dmd/blob/34c57751f8f50f623740387599f02c4ace34ee6a/compiler/src/dmd/root/port.d#L91
[2]: https://github.com/dlang/dmd/blob/34c57751f8f50f623740387599f02c4ace34ee6a/compiler/src/dmd/root/port.d#L114
Comment #4 by kinke — 2023-04-25T12:44:29Z
(In reply to Caleb Xu from comment #3)
> I tried this simple C program (based on strto{d,f}
> invocations from [1], [2]) on a machine with the new Xcode and it outputs
> four "true"s as expected. So far this seems to work, at least as far as not
> throwing an error when parsing the strings into float/double:
What matters is whether strtof/d sets `errno` to ERANGE (e.g., https://github.com/dlang/dmd/blob/34c57751f8f50f623740387599f02c4ace34ee6a/compiler/src/dmd/root/port.d#L94).
Comment #5 by maxhaton — 2023-04-25T22:47:54Z
The weird thing is that it *does* return the value still, which I think the standard doesn't really want it to due.
Comment #6 by maxhaton — 2023-04-26T00:20:13Z
It isn't enough to check ERANGE - underflow can (I think it's implementation defined) trigger ERANGE but if the result is nonzero it's still supposed to be correctly rounded.
Comment #7 by calebcenter — 2023-04-26T03:41:50Z
Good catch, the example program was not checking errno. I've repeated the test with a slightly modified example program:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main() {
float float1 = strtof("0x0.8p-126f", NULL);
printf("%s\n", (float1 != 0) ? "true" : "false");
printf("%d\n", errno);
float float2 = strtof("0x0.555556p-126f", NULL);
printf("%s\n", (float2 != 0) ? "true" : "false");
printf("%d\n", errno);
double double1 = strtod("0x0.8p-1022", NULL);
printf("%s\n", (double1 != 0) ? "true" : "false");
printf("%d\n", errno);
double double2 = strtod("0x0.5555555555555p-1022", NULL);
printf("%s\n", (double2 != 0) ? "true" : "false");
printf("%d\n", errno);
}
This time, the output with the newer Xcode toolchain is:
true
34
true
34
true
34
true
34
So looks like errno is indeed ERANGE here.
Comment #8 by maxhaton — 2023-04-26T03:56:36Z
I can't find anything that obviously changed in the apple source but they don't do changelogs so hard to guess.
I'll patch the compiler soon tho. Subnormal non-hex floats should probably warn
Comment #9 by kinke — 2023-04-26T11:32:03Z
(In reply to mhh from comment #5)
> The weird thing is that it *does* return the value still, which I think the
> standard doesn't really want it to due.
Does it? DMD doesn't use the value at all, it uses the `strtold` result. It just uses strtof/d to check for over/underflows of non-real literals.
> It isn't enough to check ERANGE - underflow can (I think it's implementation defined) trigger ERANGE but if the result is nonzero it's still supposed to be correctly rounded.
https://en.cppreference.com/w/c/string/byte/strtof says it returns HUGE_VAL when setting ERANGE.
> I've repeated the test with a slightly modified example program
[You'd need to reset `errno` before each call for a proper test.]
Comment #10 by maxhaton — 2023-04-26T11:44:08Z
Yes it does.
Huge val (i.e. infinity) is returned on overflow when the number is too large but not when it underflows which is the case here.
The result of an underflow is nonetheless still a valid finite float, but with less precision in the mantissa. So it needs to check the result too if ERANGE is returned.
Microsoft don't define underflow in their docs so it's possible their behaviour is different.
Comment #11 by calebcenter — 2023-04-26T12:00:27Z
(In reply to kinke from comment #9)
> [You'd need to reset `errno` before each call for a proper test.]
Thanks for pointing this out, I tried once more with resetting errno to 0 after each set of print statements and the errno is still set to 34 each time.
Comment #12 by dlang-bot — 2023-04-27T16:25:01Z
@maxhaton created dlang/dmd pull request #15139 "Fix Issue 23846 - Respect the C standard when it comes to string to f…" fixing this issue:
- Fix Issue 23846 - Respect the C standard when it comes to string to float ERRNO and return value
https://github.com/dlang/dmd/pull/15139
Comment #13 by dlang-bot — 2023-04-28T14:27:44Z
dlang/dmd pull request #15139 "Fix Issue 23846 - Respect the C standard when it comes to string to f…" was merged into master:
- 5266db2696f854adb36687e984096228946a76a6 by Max Haughton:
Fix Issue 23846 - Respect the C standard when it comes to string to float ERRNO and return value
https://github.com/dlang/dmd/pull/15139
Comment #14 by thinkunix — 2023-05-09T05:59:35Z
I wanted to comment that I saw this same issue when trying to compile
dmd-2.102.2 on an older Linux x86_64 system. I originally posted on
the dlang forum here:
https://forum.dlang.org/post/[email protected]
My post received no responses. I guess people don't compile dmd themselves.
dmd-2.102.2 through anything before dmd-2.104.0-beta.1 would not compile
phobos, ending with these errors:
std/math/exponential.d(3782): Error: number `0x0.8p-126f` is not representable as a `float`
std/math/exponential.d(3784): Error: number `0x0.555556p-126f` is not representable as a `float`
std/math/exponential.d(3795): Error: number `0x0.8p-1022` is not representable as a `double`
std/math/exponential.d(3797): Error: number `0x0.5555555555555p-1022` is not representable as a `double`
I just tested dmd-2.104.0-beta.1 and it now builds successfully on my older
system, using and installed dmd-2.102.1 to do the build. Thanks for the fix!