Skip to content

Commit ffe8b24

Browse files
committed
C_LAPACK: take the float format from <float.h> in dlamch/slamch
INSTALL/dlamch.c and INSTALL/slamch.c are f2c translations of the deprecated dlamchf77.f and slamchf77.f, which determine the floating point format at run time by probing in dlamc1/dlamc2 rather than reading it from the environment. The current dlamch.f and slamch.f use the Fortran 90 inquiry intrinsics (EPSILON, TINY, HUGE, DIGITS, MINEXPONENT, MAXEXPONENT, RADIX) instead, but f2c cannot translate those, so the C LAPACK selected by NOFORTRAN=1 has been left with the older probing implementation. The probe is only correct if double intermediates are genuinely rounded to double. That does not hold on x87. Building 32 bit for a target without SSE2, so with -mfpmath=387, and with gcc 16, the intermediates stay in registers and the probe measures the 80 bit register format: it reports emin/emax as -16381/16384, and a mantissa width that follows the caller's x87 precision control bits (64 at extended precision, 53 at double). Written back as doubles, rmin underflows to 0 and rmax overflows to +Inf, so dlamch('S') and dlamch('U') return 0 and dlamch('O') returns +Inf. Everything that scales by those values is then wrong, mostly silently. The first symptom to surface was a floating point exception rather than a wrong answer: dsbevx computes safmin = dlamch('S') eps = dlamch('P') smlnum = safmin / eps bignum = 1 / smlnum so a zero safe minimum makes smlnum zero and the next line divides by zero. Callers that unmask the divide by zero exception get a hard failure there; callers that do not get whatever the wrong scaling produces. Replace the probe with the <float.h> constants, mirroring the values the current dlamch.f and slamch.f return. This fixes two lesser problems at the same time: rmach was left uninitialised when cmach matched nothing, where dlamch.f returns zero; and the cached static results made both routines unsafe to call concurrently on first use. dlamc1-dlamc5 and slamc1-slamc5 are left in place. They become unreachable from dlamch/slamch, but dlamc3 and slamc3 have callers of their own in dlaed3, dlaed9, dlals0, dlasd3, dlasd8 and their complex equivalents, where they serve as optimiser barriers.
1 parent d9f362a commit ffe8b24

2 files changed

Lines changed: 58 additions & 98 deletions

File tree

lapack-netlib/INSTALL/dlamch.c

Lines changed: 29 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include <float.h>
12
#include <math.h>
23
#include <stdlib.h>
34
#include <string.h>
@@ -354,29 +355,13 @@ static doublereal c_b32 = 0.;
354355
/* ===================================================================== */
355356
doublereal dlamch_(char *cmach)
356357
{
357-
/* Initialized data */
358-
359-
static logical first = TRUE_;
360-
361358
/* System generated locals */
362-
integer i__1;
363359
doublereal ret_val;
364360

365361
/* Local variables */
366-
static doublereal base;
367-
integer beta;
368-
static doublereal emin, prec, emax;
369-
integer imin, imax;
370-
logical lrnd;
371-
static doublereal rmin, rmax, t;
372362
doublereal rmach;
373363
extern logical lsame_(char *, char *);
374-
doublereal small;
375-
static doublereal sfmin;
376-
extern /* Subroutine */ int dlamc2_(integer *, integer *, logical *,
377-
doublereal *, integer *, doublereal *, integer *, doublereal *);
378-
integer it;
379-
static doublereal rnd, eps;
364+
doublereal small, sfmin, eps;
380365

381366

382367
/* -- LAPACK auxiliary routine (version 3.7.0) -- */
@@ -385,57 +370,52 @@ doublereal dlamch_(char *cmach)
385370
/* April 2012 */
386371

387372

388-
if (first) {
389-
dlamc2_(&beta, &it, &lrnd, &eps, &imin, &rmin, &imax, &rmax);
390-
base = (doublereal) beta;
391-
t = (doublereal) it;
392-
if (lrnd) {
393-
rnd = 1.;
394-
i__1 = 1 - it;
395-
eps = pow_di(&base, &i__1) / 2;
396-
} else {
397-
rnd = 0.;
398-
i__1 = 1 - it;
399-
eps = pow_di(&base, &i__1);
400-
}
401-
prec = eps * base;
402-
emin = (doublereal) imin;
403-
emax = (doublereal) imax;
404-
sfmin = rmin;
405-
small = 1. / rmax;
373+
/* The values below are those returned by the current dlamch.f, which */
374+
/* obtains them from the Fortran 90 inquiry intrinsics. They replace the */
375+
/* dlamc1/dlamc2 probe of the deprecated dlamchf77.f, which measures the */
376+
/* format at run time and is only correct if double intermediates are */
377+
/* genuinely rounded to double. That does not hold on x87: the probe */
378+
/* measures the 80 bit register format instead, and dlamch then returns 0 */
379+
/* for the safe minimum and +Inf for the overflow threshold. Reading the */
380+
/* constants also makes this routine thread safe, which the cached, */
381+
/* probed version was not. */
382+
383+
eps = DBL_EPSILON * 0.5;
384+
385+
if (lsame_(cmach, "E")) {
386+
rmach = eps;
387+
} else if (lsame_(cmach, "S")) {
388+
sfmin = DBL_MIN;
389+
small = 1. / DBL_MAX;
406390
if (small >= sfmin) {
407391

408392
/* Use SMALL plus a bit, to avoid the possibility of rounding */
409393
/* causing overflow when computing 1/sfmin. */
410394

411395
sfmin = small * (eps + 1.);
412396
}
413-
}
414-
415-
if (lsame_(cmach, "E")) {
416-
rmach = eps;
417-
} else if (lsame_(cmach, "S")) {
418397
rmach = sfmin;
419398
} else if (lsame_(cmach, "B")) {
420-
rmach = base;
399+
rmach = FLT_RADIX;
421400
} else if (lsame_(cmach, "P")) {
422-
rmach = prec;
401+
rmach = eps * FLT_RADIX;
423402
} else if (lsame_(cmach, "N")) {
424-
rmach = t;
403+
rmach = DBL_MANT_DIG;
425404
} else if (lsame_(cmach, "R")) {
426-
rmach = rnd;
405+
rmach = 1.;
427406
} else if (lsame_(cmach, "M")) {
428-
rmach = emin;
407+
rmach = DBL_MIN_EXP;
429408
} else if (lsame_(cmach, "U")) {
430-
rmach = rmin;
409+
rmach = DBL_MIN;
431410
} else if (lsame_(cmach, "L")) {
432-
rmach = emax;
411+
rmach = DBL_MAX_EXP;
433412
} else if (lsame_(cmach, "O")) {
434-
rmach = rmax;
413+
rmach = DBL_MAX;
414+
} else {
415+
rmach = 0.;
435416
}
436417

437418
ret_val = rmach;
438-
first = FALSE_;
439419
return ret_val;
440420

441421
/* End of DLAMCH */

lapack-netlib/INSTALL/slamch.c

Lines changed: 29 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include <float.h>
12
#include <math.h>
23
#include <stdlib.h>
34
#include <string.h>
@@ -353,29 +354,13 @@ static real c_b32 = 0.f;
353354
/* ===================================================================== */
354355
real slamch_(char *cmach)
355356
{
356-
/* Initialized data */
357-
358-
static logical first = TRUE_;
359-
360357
/* System generated locals */
361-
integer i__1;
362358
real ret_val;
363359

364360
/* Local variables */
365-
static real base;
366-
integer beta;
367-
static real emin, prec, emax;
368-
integer imin, imax;
369-
logical lrnd;
370-
static real rmin, rmax, t;
371361
real rmach;
372362
extern logical lsame_(char *, char *);
373-
real small;
374-
static real sfmin;
375-
extern /* Subroutine */ int slamc2_(integer *, integer *, logical *, real
376-
*, integer *, real *, integer *, real *);
377-
integer it;
378-
static real rnd, eps;
363+
real small, sfmin, eps;
379364

380365

381366
/* -- LAPACK auxiliary routine (version 3.7.0) -- */
@@ -384,57 +369,52 @@ real slamch_(char *cmach)
384369
/* April 2012 */
385370

386371

387-
if (first) {
388-
slamc2_(&beta, &it, &lrnd, &eps, &imin, &rmin, &imax, &rmax);
389-
base = (real) beta;
390-
t = (real) it;
391-
if (lrnd) {
392-
rnd = 1.f;
393-
i__1 = 1 - it;
394-
eps = pow_ri(&base, &i__1) / 2;
395-
} else {
396-
rnd = 0.f;
397-
i__1 = 1 - it;
398-
eps = pow_ri(&base, &i__1);
399-
}
400-
prec = eps * base;
401-
emin = (real) imin;
402-
emax = (real) imax;
403-
sfmin = rmin;
404-
small = 1.f / rmax;
372+
/* The values below are those returned by the current slamch.f, which */
373+
/* obtains them from the Fortran 90 inquiry intrinsics. They replace the */
374+
/* slamc1/slamc2 probe of the deprecated slamchf77.f, which measures the */
375+
/* format at run time and is only correct if intermediates are genuinely */
376+
/* rounded to single. That does not hold on x87: the probe measures the */
377+
/* 80 bit register format instead, and slamch then returns 0 for the safe */
378+
/* minimum and +Inf for the overflow threshold. Reading the constants */
379+
/* also makes this routine thread safe, which the cached, probed version */
380+
/* was not. */
381+
382+
eps = FLT_EPSILON * 0.5f;
383+
384+
if (lsame_(cmach, "E")) {
385+
rmach = eps;
386+
} else if (lsame_(cmach, "S")) {
387+
sfmin = FLT_MIN;
388+
small = 1.f / FLT_MAX;
405389
if (small >= sfmin) {
406390

407391
/* Use SMALL plus a bit, to avoid the possibility of rounding */
408392
/* causing overflow when computing 1/sfmin. */
409393

410394
sfmin = small * (eps + 1.f);
411395
}
412-
}
413-
414-
if (lsame_(cmach, "E")) {
415-
rmach = eps;
416-
} else if (lsame_(cmach, "S")) {
417396
rmach = sfmin;
418397
} else if (lsame_(cmach, "B")) {
419-
rmach = base;
398+
rmach = FLT_RADIX;
420399
} else if (lsame_(cmach, "P")) {
421-
rmach = prec;
400+
rmach = eps * FLT_RADIX;
422401
} else if (lsame_(cmach, "N")) {
423-
rmach = t;
402+
rmach = FLT_MANT_DIG;
424403
} else if (lsame_(cmach, "R")) {
425-
rmach = rnd;
404+
rmach = 1.f;
426405
} else if (lsame_(cmach, "M")) {
427-
rmach = emin;
406+
rmach = FLT_MIN_EXP;
428407
} else if (lsame_(cmach, "U")) {
429-
rmach = rmin;
408+
rmach = FLT_MIN;
430409
} else if (lsame_(cmach, "L")) {
431-
rmach = emax;
410+
rmach = FLT_MAX_EXP;
432411
} else if (lsame_(cmach, "O")) {
433-
rmach = rmax;
412+
rmach = FLT_MAX;
413+
} else {
414+
rmach = 0.f;
434415
}
435416

436417
ret_val = rmach;
437-
first = FALSE_;
438418
return ret_val;
439419

440420
/* End of SLAMCH */

0 commit comments

Comments
 (0)