. "$BST_LIB" # mpfr 4.2.1 -- GNU multiple-precision floating point with CORRECT ROUNDING # (poky recipes-support/mpfr). See mpfr_%.bbappend for what the port adds and why. # # --------------------------------------------------------------------------- # WHERE THE EVIDENCE COMES FROM # # mpfr ships as -dev/-staticdev only: /usr/lib/libmpfr.a and /usr/include/mpfr.h, # no binary at all. So "the files are present" would have been the whole of the # on-target evidence, which is the BRINTOS-217 shape -- oniguruma compiled and # packaged perfectly and then returned silently wrong offsets to every caller. # A numeric library is the worst possible package to accept that for: wrong # arithmetic does not crash, it just answers. # # So data/mpfr-probe (src/mpfr_probe.c) is cross-linked with wasmcc-glibc # against THE VERY ARCHIVES THAT SHIP (/usr/lib/libmpfr.a and /usr/lib/libgmp.a) # and drives the library: exact rational arithmetic, all four rounding modes, # six transcendentals, the special values, string parsing, integer conversion # across the 32-bit boundary, gmp interop and precision change. # # --------------------------------------------------------------------------- # WHY THE EXPECTED VALUES ARE STRONGER HERE THAN ELSEWHERE # # Most probes in this suite are scored against a native x86_64 capture, and that # capture DEFINES the expected values. mpfr does not need that, because its # entire contract is correct rounding: at a stated precision and rounding mode # the result is uniquely determined by the value being approximated -- not by # version, platform, limb width or compiler. Every MP- row below is the # mathematically correct answer. # # data/mpfr-native.txt is generated anyway (src/mpfr_mknative.sh, from the same # tarball the recipe fetches, sha256 277807353a67..., configured the same way), # but as CORROBORATION rather than as the oracle. The distinction is not # academic: when the control was first generated it was cross-checked against a # separate build of mpfr 4.1.0, and the two agreed byte-for-byte across all 32 # rows -- which is what correct rounding predicts and what a merely-observed # oracle could never have told us. # # A difference on an MP- row therefore means the arithmetic is WRONG, not that # the platforms legitimately diverge. There is no "expected red" here. # # --------------------------------------------------------------------------- # THE ROWS WRITTEN FOR PLATFORM REASONS, AND WHAT EACH IS HOLDING # # mpfr/limb-bits mp_limb_t is 32 bits here against the control's 64, so # every carry chain, normalisation and shift in both mpfr # and gmp runs its 32-bit path. This is the single fact # that makes the arithmetic rows worth taking to 40-50 # digits instead of five: a 32-bit limb bug lands in the # TAIL of a value, where a smoke test never looks. # # mpfr/ld-mant-dig long double is binary128 (113-bit mantissa) on this arm, # mpfr/ld-roundtrip unlike most 32-bit targets and unlike the x87 control's # 64. So mpfr_set_ld crosses a 113-bit format in software. # The VALUE is platform-dependent and is only reported; # what is asserted is that the round-trip still agrees # with 1/3 to 30 digits, which a truncation to double # (~17) would fail on either arm. # # mpfr/buildopt-float128 # The regression guard for the whole port. # --disable-float128 is why mpfr compiles here at all: # without it mpfr's configure emits -D_Float128=__float128 # on every compile, which rewrites glibc's # `typedef long double _Float128` in bits/floatn.h into # two type specifiers and kills every object in src/. # If the flag is ever dropped this row does not go red -- # the package stops building. It is asserted so the # intent is recorded where the next person will read it. # # --------------------------------------------------------------------------- # FORK BUDGET # # The probe runs ONCE and both payloads are read ONCE into shell variables; # every assertion below is a `case` glob or a check_eq against those variables, # so there is no fork per case. This is the 81-jansson shape and it is # deliberate -- on this runtime the forking is the cost, not the work, and the # cjson suite next door was killed by the per-suite bound at 156 cases written # as check_out+cat. # --------------------------------------------------------------------------- lib=/usr/lib/libmpfr.a hdr=/usr/include/mpfr.h shipped=$BST_DATA/mpfr-probe control=$BST_DATA/mpfr-native.txt tmp=${TMPDIR:-/tmp} probe=$tmp/bst-mpfr-probe.$$ nl=' ' # --- what the port ships ------------------------------------------------------ if [ ! -f "$lib" ]; then bst_skip mpfr/present "no $lib on this image" exit 0 fi bst_ok mpfr/present check mpfr/header-present test -f "$hdr" check mpfr/probe-present test -f "$shipped" check mpfr/control-present test -f "$control" # The archive is the deliverable, so assert it is a real one rather than an # empty stub -- a 0-byte .a satisfies `test -f` and fails nothing else here. sz=$(wc -c < "$lib" 2>/dev/null || echo 0) if [ "$sz" -gt 100000 ]; then bst_ok mpfr/archive-nonempty else bst_notok mpfr/archive-nonempty "libmpfr.a is $sz bytes"; fi # mpfr.h gates the float128 entry points behind MPFR_WANT_FLOAT128, so a # consumer that does not define it never sees them. That is the header half of # the same fact mpfr/buildopt-float128 asserts at runtime. if grep -q 'ifdef MPFR_WANT_FLOAT128' "$hdr" 2>/dev/null; then bst_ok mpfr/header-float128-gated else bst_notok mpfr/header-float128-gated "MPFR_WANT_FLOAT128 guard missing from $hdr"; fi # --- run the library ---------------------------------------------------------- # data/ is packaged with `install -m 0644`, so the probe ships without an exec # bit and is staged out before it runs -- the same copy+chmod every other # probe-consuming suite does. A failed stage still falls through, so the cases # below report what did not run instead of vanishing from the count. if cp "$shipped" "$probe" 2>/dev/null && chmod +x "$probe" 2>/dev/null; then bst_ok mpfr/probe-stage out=$("$probe" 2>&1); rc=$? else bst_notok mpfr/probe-stage "cannot stage $shipped" out=''; rc=127 fi rm -f "$probe" if [ "$rc" -eq 0 ]; then bst_ok mpfr/probe-exit else bst_notok mpfr/probe-exit "rc=$rc $(_bst_1line "$out")"; fi case "$out" in *ALL-MPFR-OK*) bst_ok mpfr/probe-all-ok ;; *) bst_notok mpfr/probe-all-ok "probe did not reach its own success line: $(_bst_1line "$out")" ;; esac # --- the whole-block comparison ---------------------------------------------- # Both sides reduced to their MP- rows and compared as one string. This is the # case that cannot be fooled by a probe that stops early: a truncated run has # fewer rows and fails here even if every row it did print was right. got=$(printf '%s\n' "$out" | grep '^MP-') want=$(grep '^MP-' "$control") if [ "$got" = "$want" ]; then bst_ok mpfr/native-byte-identical else # Name the first row that differs rather than dumping both blocks -- the # per-case checks below carry the detail, and a 32-row diff does not fit in # a result line anyway. first=$(printf '%s\n' "$want" | while IFS= read -r w; do n=${w%% *} g=$(printf '%s\n' "$got" | grep "^$n " || echo "") [ "$g" = "$w" ] || { printf '%s: want [%s] got [%s]' "$n" "${w#* }" "${g#* }"; break; } done) bst_notok mpfr/native-byte-identical "$(_bst_1line "${first:-row counts differ}")" fi ngot=$(printf '%s\n' "$got" | grep -c '^MP-') nwant=$(printf '%s\n' "$want" | grep -c '^MP-') check_eq mpfr/row-count "$nwant" "$ngot" # --- named cases, so a red run says WHICH arithmetic broke -------------------- # Each reads the already-captured output; no forks, no re-runs. The value is # taken from the control rather than typed here, so there is exactly one place # an expected number lives. mp() { # mp -> that row's value, from a string printf '%s\n' "$2" | while IFS= read -r l; do case "$l" in "MP-$1 "*) printf '%s\n' "${l#MP-$1 }"; break ;; esac done } for case_name in third-200 twothirds-200 \ rnd-n rnd-z rnd-u rnd-d \ rndneg-n rndneg-z rndneg-u rndneg-d \ pi e log2 sqrt2 sin1 atan1 \ nan inf-pos inf-neg zero-neg zero-pos nan-unordered \ str-parse str-reject si-min32 ui-max32 \ mpz-in mpz-out pi-24bit cmp-lt cmp-eq \ buildopt-float128 do check_eq "mpfr/$case_name" "$(mp "$case_name" "$want")" "$(mp "$case_name" "$got")" done # --- the platform rows: reported, and asserted on their own terms ------------- mpv() { printf '%s\n' "$out" | while IFS= read -r l; do case "$l" in "MPV-$1 "*) printf '%s\n' "${l#MPV-$1 }"; break ;; esac done } check_eq mpfr/version "4.2.1" "$(mpv version)" check_eq mpfr/limb-bits "32" "$(mpv limb-bits)" check_eq mpfr/ld-mant-dig "113" "$(mpv ld-mant-dig)" # long double round-trip: the value differs by format, the accuracy must not. # 1/3 through a 113-bit long double must still agree with 1/3 to 30 digits; # a silent truncation to double would leave ~17. ldr=$(mpv ld-roundtrip) case "$ldr" in 333333333333333333333333333333*) bst_ok mpfr/ld-roundtrip ;; *) bst_notok mpfr/ld-roundtrip "1/3 through long double lost precision early: $ldr" ;; esac printf 'MPFR platform rows: version=%s limbs=%s ld_mant_dig=%s prec_max=%s tls=%s gmp=%s\n' \ "$(mpv version)" "$(mpv limb-bits)" "$(mpv ld-mant-dig)" \ "$(mpv prec-max)" "$(mpv tls)" "$(mpv gmp-version)"