v0.2.3
Pre-releaseAdded
-
Software binary floating point in
rucc-base, with the six formats the compiler has to produce:binary16,bfloat16,binary32,binary64, the x87 eighty bit format with its stored leading significand bit, andbinary128. A compiler cannot ask the machine it runs on what a floating constant means, because the host may not have the format at all,long doubleis eighty bits on x86-64 and a hundred and twenty eight on AArch64 Linux and sixty four on Apple, andstrtodis the host's libc rather than the target's semantics. Reproducible output means the same source gives the same bits whoever compiles it, so the conversion is done here in integer arithmetic. It is correctly rounded, round to nearest with ties to even, using an exact decimal that is scaled by powers of two until the value is in the range the significand can be read off, which is the algorithm Go'sstrconvuses and the one Rust's own parser falls back to. A naivemantissa * 10^exponentin double precision is wrong in the last place for a noticeable fraction of literals, and the last place is exactly what a differential test against another compiler notices. Thebinary64andbinary32answers are checked against Rust's own correctly rounded parser over four thousand generated numbers as well as the hard cases, including the seven hundred and sixty seven digit halfway value that a conversion truncating its input gets wrong, and the x87 andbinary128answers were measured by compiling the constants with gcc 13.3 and reading the bytes back out of the program. Hexadecimal constants are exact by construction and are rounded once at the end. Arithmetic is not here yet, since a constant does not need it; it comes with the constant evaluator. -
Floating constants in
rucc-lex, the other half of what a preprocessing number can turn into. A floating constant has none of the table walk an integer one has, because the suffix names the type outright, and what it has instead is a suffix list far longer than the standard's three and a conversion that has to be right to the last bit. The suffixes were measured on gcc 13.3 rather than recalled:qis__float128,wis the x87__float80,dis adoublewritten the long way,f16throughf128are the_FloatNtypes,f32xandf64xthe_FloatNxones, and anior ajon either side of the type makes the constant imaginary. Two of the answers are not what the names suggest:_Float32xis plaindoubleand_Float64xis the x87 format, so0.1f64xand0.1lare the same bits on x86-64 Linux. The case rules are their own small grammar, since thefof a_FloatNsuffix may be either case and the trailingxmay not, soF64xis a constant andf64Xis not, and the two letters of a decimal float suffix have to agree, soddandDDare constants anddDis not. Every extension suffix is accepted in every dialect including C89, with a remark for the caller holding the span, which is what gcc does. Decimal floating constants are recognised and refused with an error that says so, because there is no decimal floating value anywhere in this compiler to put one in yet. A constant too large for its type becomes an infinity and one too small becomes a zero, both with a remark, which is the pair of warnings gcc gives. -
Character constants and string literals in
rucc-lex, which is the last piece of phase 7 that is about spellings and the first one whose answer depends on the target in a way a reader would not guess. A literal arrives as the bytes the user wrote and leaves as elements, and what an element is comes from the encoding prefix and fromwchar_t, soL"a"followed by an emoji is two elements on Linux and three on Windows, where a wide string is UTF-16 and the character needs a surrogate pair. The escapes divide into two kinds and the division is the whole design: an escape that names a character gets encoded in the literal's encoding, so a plain"e-acute"is the two bytesc3 a9, and an escape that writes a value is that element as written and is truncated to the element with a remark when it does not fit, so'\x1ff'is minus one andL'\x1ff'is five hundred and eleven. Octal stops after three digits and hexadecimal runs as far as the digits go, which is why"\1234"is two characters and"\x41z"is two as well. A plain character constant is anintand its single character is converted through plaincharfirst, so'\xff'is minus one on x86-64 Linux and two hundred and fifty five on AArch64, where plaincharis unsigned. More than one character shifts them together and the ones past the width of the type fall off the front, so'abcde'is0x62636465, and gcc reports that case as too long instead of as multi-character rather than as well as it, which is a distinction only measurement gives you. Every value here was measured against gcc 13.3 on x86-64 Linux rather than recalled. There is one deliberate divergence: a universal character name above the end of Unicode is refused here and in clang, where gcc warns and encodes the value as though UTF-8 went that far. -
wchar_widthandwchar_is_signedinrucc-target, which is where the literals get their answer. The two fields split the targets in different directions and neither follows from anything else already there: the width is sixteen on Windows and thirty two everywhere else, and the sign follows the psABI's rule for plainchar, sowchar_tis a signedinton x86-64 Linux and anunsigned inton AArch64 Linux andL'\xffffffff'is minus one on one and four billion on the other. The predefined macros inrucc-ppnow derive__WCHAR_TYPE__,__WCHAR_MAX__,__WCHAR_MIN__and__SIZEOF_WCHAR_T__from these two fields rather than matching on the triple a second time, so the lexer and the macros cannot come to different conclusions about the same target. -
long_double_formatinrucc-target, because the width oflong doubledoes not say what it is. It is a hundred and twenty eight bits wide on x86-64 Linux and on AArch64 Linux and those are not the same type: one is the x87 eighty bit format padded out to sixteen bytes and the other is true quad precision with a hundred and thirteen bits of significand, so1.0lis0x3fff8000000000000000on one and0x3fff0000000000000000000000000000on the other. Anything that converts a constant or folds one has to know which, and until now nothing could tell.
What's Changed
- Add software binary floating point by @tamnd in #59
- Add floating constants to the lexer by @tamnd in #60
- Add character constants and string literals to the lexer by @tamnd in #61
- Release 0.2.3 by @tamnd in #62
Full Changelog: v0.2.2...v0.2.3