Issue 23846 - std.math can't compile under macos rosetta
Summary: std.math can't compile under macos rosetta
Status: RESOLVED FIXED
Alias: None
Product: D
Classification: Unclassified
Component: phobos (show other issues)
Version: D2
Hardware: Other Mac OS X
: P1 major
Assignee: No Owner
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2023-04-19 15:18 UTC by John Colvin
Modified: 2023-05-09 05:59 UTC (History)
4 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description John Colvin 2023-04-19 15:18:44 UTC
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 Caleb Xu 2023-04-22 17:14:36 UTC
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 kinke 2023-04-22 18:57:39 UTC
(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 Caleb Xu 2023-04-23 11:55:40 UTC
(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 kinke 2023-04-25 12:44:29 UTC
(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 mhh 2023-04-25 22:47:54 UTC
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 mhh 2023-04-26 00:20:13 UTC
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 Caleb Xu 2023-04-26 03:41:50 UTC
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 mhh 2023-04-26 03:56:36 UTC
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 kinke 2023-04-26 11:32:03 UTC
(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 mhh 2023-04-26 11:44:08 UTC
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 Caleb Xu 2023-04-26 12:00:27 UTC
(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 Dlang Bot 2023-04-27 16:25:01 UTC
@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 Dlang Bot 2023-04-28 14:27:44 UTC
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 Scot 2023-05-09 05:59:35 UTC
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/mailman.9077.1678347256.31357.digitalmars-d-learn@puremagic.com

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!