Skip to content

Commit 3ac6b06

Browse files
committed
Fix FloatingPoint nextUp/Down on armv7.
Rigorously implement flush-to-zero behavior. This fixes several recent unit test failures on armv7 hardware.
1 parent 1752b11 commit 3ac6b06

File tree

2 files changed

+45
-1
lines changed

2 files changed

+45
-1
lines changed

stdlib/public/core/FloatingPointTypes.swift.gyb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,15 @@ extension ${Self}: BinaryFloatingPoint {
464464
public var nextUp: ${Self} {
465465
if isNaN { return self }
466466
if sign == .minus {
467+
#if arch(arm)
468+
// On arm, subnormals are flushed to zero.
469+
if (exponentBitPattern == 1 && significandBitPattern == 0) ||
470+
(exponentBitPattern == 0 && significandBitPattern != 0) {
471+
return ${Self}(sign: .minus,
472+
exponentBitPattern: 0,
473+
significandBitPattern: 0)
474+
}
475+
#endif
467476
if significandBitPattern == 0 {
468477
if exponentBitPattern == 0 {
469478
return .leastNonzeroMagnitude
@@ -485,7 +494,6 @@ extension ${Self}: BinaryFloatingPoint {
485494
#if arch(arm)
486495
// On arm, subnormals are skipped.
487496
if exponentBitPattern == 0 {
488-
_sanityCheck(self < .leastNonzeroMagnitude, "subnormal out of range")
489497
return .leastNonzeroMagnitude
490498
}
491499
#endif

test/1_stdlib/FloatingPoint.swift.gyb

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,9 @@ FloatingPoint.test("Float/staticProperties") {
107107
expectBitwiseEqual(0x1.921f_b6__p1, Ty.pi)
108108
expectBitwiseEqual(0x1.0p-23, Ty.ulpOfOne)
109109
expectBitwiseEqual(0x1.0p-126, Ty.leastNormalMagnitude)
110+
#if !arch(arm)
110111
expectBitwiseEqual(0x1.0p-149, Ty.leastNonzeroMagnitude)
112+
#endif
111113

112114
// From the BinaryFloatingPoint protocol.
113115
expectEqual(8, Ty.exponentBitCount)
@@ -180,7 +182,9 @@ FloatingPoint.test("Double/staticProperties") {
180182
expectBitwiseEqual(0x1.921f_b544_42d1_8__p1, Ty.pi)
181183
expectBitwiseEqual(0x1.0p-52, Ty.ulpOfOne)
182184
expectBitwiseEqual(0x1.0p-1022, Ty.leastNormalMagnitude)
185+
#if !arch(arm)
183186
expectBitwiseEqual(0x1.0p-1074, Ty.leastNonzeroMagnitude)
187+
#endif
184188

185189
// From the BinaryFloatingPoint protocol.
186190
expectEqual(11, Ty.exponentBitCount)
@@ -243,6 +247,24 @@ FloatingPoint.test("Double/HashValueZero") {
243247
expectEqual(zero.hashValue, negativeZero.hashValue)
244248
}
245249

250+
#if arch(arm)
251+
% for FloatSelf in ['Float32', 'Float64']:
252+
func testSubNormalFlush(_ f: ${FloatSelf}) {
253+
if !f.isSubnormal {
254+
return
255+
}
256+
switch f.sign {
257+
case .plus:
258+
expectBitwiseEqual(.leastNonzeroMagnitude, f.nextUp)
259+
expectBitwiseEqual(0.0, f.nextDown)
260+
case .minus:
261+
expectBitwiseEqual(-0.0, f.nextUp)
262+
expectBitwiseEqual(-.leastNonzeroMagnitude, f.nextDown)
263+
}
264+
}
265+
% end
266+
#endif
267+
246268
let floatNextUpDownTests: [(Float, Float)] = [
247269
(.nan, .nan),
248270
(.greatestFiniteMagnitude, .infinity),
@@ -254,6 +276,13 @@ let floatNextUpDownTests: [(Float, Float)] = [
254276
FloatingPoint.test("Float.nextUp, .nextDown")
255277
.forEach(in: floatNextUpDownTests) {
256278
(prev, succ) in
279+
#if arch(arm)
280+
if prev.isSubnormal || succ.isSubnormal {
281+
testSubNormalFlush(prev)
282+
testSubNormalFlush(succ)
283+
return
284+
}
285+
#endif
257286
expectBitwiseEqual(succ, prev.nextUp)
258287
expectBitwiseEqual(prev, succ.nextDown)
259288
expectBitwiseEqual(-succ, (-prev).nextDown)
@@ -271,6 +300,13 @@ let doubleNextUpDownTests: [(Double, Double)] = [
271300
FloatingPoint.test("Double.nextUp, .nextDown")
272301
.forEach(in: doubleNextUpDownTests) {
273302
(prev, succ) in
303+
#if arch(arm)
304+
if prev.isSubnormal || succ.isSubnormal {
305+
testSubNormalFlush(prev)
306+
testSubNormalFlush(succ)
307+
return
308+
}
309+
#endif
274310
expectBitwiseEqual(succ, prev.nextUp)
275311
expectBitwiseEqual(prev, succ.nextDown)
276312
expectBitwiseEqual(-succ, (-prev).nextDown)

0 commit comments

Comments
 (0)