diff options
| author | Cliff Click <cliffc@acm.org> | 2026-10-28 15:05:08 -0700 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2026-10-28 15:05:08 -0700 |
| commit | 50924bbe7cfeecfe7aec1885dae9bbda0013ac1b (patch) | |
| tree | 2d04316faa3c73e2a2586de8f95f9b9fb248425f /chapter18 | |
| parent | 86ce5bc5d5018dcb62054d214b23f7a59b9c0d7d (diff) | |
| download | Simple-50924bbe7cfeecfe7aec1885dae9bbda0013ac1b.tar.gz Simple-50924bbe7cfeecfe7aec1885dae9bbda0013ac1b.zip | |
Remove _phase checks from Arith & Compute
Fix some monotonicty bugs, rounding bugs, in float ops
Diffstat (limited to 'chapter18')
5 files changed, 72 insertions, 0 deletions
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/AddFNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/AddFNode.java index fd713156..3b37dc84 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/AddFNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/AddFNode.java @@ -14,10 +14,13 @@ public class AddFNode extends Node { @Override public Type compute() { + if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return Type.TOP; if (in(1)._type instanceof TypeFloat i0 && in(2)._type instanceof TypeFloat i1) { if (i0.isConstant() && i1.isConstant()) return TypeFloat.constant(i0.value()+i1.value()); + // Two f32 inputs can still produce a result needing f64 precision. + return TypeFloat.F64; } return in(1)._type.meet(in(2)._type); } diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/DivFNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/DivFNode.java index a3f4318d..80b7dccf 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/DivFNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/DivFNode.java @@ -14,10 +14,13 @@ public class DivFNode extends Node { @Override public Type compute() { + if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return Type.TOP; if (in(1)._type instanceof TypeFloat i0 && in(2)._type instanceof TypeFloat i1) { if (i0.isConstant() && i1.isConstant()) return TypeFloat.constant(i0.value()/i1.value()); + // Two f32 inputs can still produce a result needing f64 precision. + return TypeFloat.F64; } return in(1)._type.meet(in(2)._type); } diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/MulFNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/MulFNode.java index 6a57f3c5..f24270cf 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/MulFNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/MulFNode.java @@ -14,10 +14,13 @@ public class MulFNode extends Node { @Override public Type compute() { + if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return Type.TOP; if (in(1)._type instanceof TypeFloat i0 && in(2)._type instanceof TypeFloat i1) { if (i0.isConstant() && i1.isConstant()) return TypeFloat.constant(i0.value()*i1.value()); + // Two f32 inputs can still produce a result needing f64 precision. + return TypeFloat.F64; } return in(1)._type.meet(in(2)._type); } diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/SubFNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/SubFNode.java index 2d218a03..4dbc141e 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/SubFNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/SubFNode.java @@ -14,10 +14,13 @@ public class SubFNode extends Node { @Override public Type compute() { + if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return Type.TOP; if (in(1)._type instanceof TypeFloat i0 && in(2)._type instanceof TypeFloat i1) { if (i0.isConstant() && i1.isConstant()) return TypeFloat.constant(i0.value()-i1.value()); + // Two f32 inputs can still produce a result needing f64 precision. + return TypeFloat.F64; } return in(1)._type.meet(in(2)._type); } diff --git a/chapter18/src/test/java/com/seaofnodes/simple/FloatPrecisionTest.java b/chapter18/src/test/java/com/seaofnodes/simple/FloatPrecisionTest.java new file mode 100644 index 00000000..6b3ddae3 --- /dev/null +++ b/chapter18/src/test/java/com/seaofnodes/simple/FloatPrecisionTest.java @@ -0,0 +1,60 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeFloat; +import java.util.ArrayList; +import java.util.Arrays; +import org.junit.Test; +import static org.junit.Assert.*; + +public class FloatPrecisionTest { + @Test public void testFloatPrecision() { + String[] ops = {"+1.0","-1.0","*3.0","/3.0"}; + long[] args = {16777216,-16777216,16777215,1}; + double[] results = {16777217.0,-16777217.0,50331645.0,1.0/3.0}; + for( int i = 0; i<ops.length; i++ ) { + String expr = "x"+ops[i]; + assertEquals(expr,Double.toString(results[i]), + evaluate("f32 x = arg; return "+expr+";",args[i])); + assertEquals(expr,Double.toString((float)results[i]), + evaluate("f32 x = arg; f32 y = "+expr+"; return y;",args[i])); + } + } + + private static String evaluate(String src, long arg) { + CodeGen code = new CodeGen(src).parse().iter(); + return Eval2.eval(code,arg); + } + + @Test public void testMonotoneCompute() { + new CodeGen(""); + ArrayList<Type> inputs = new ArrayList<>(Arrays.asList( + Type.TOP,Type.BOTTOM,TypeFloat.F32,TypeFloat.F64, + TypeFloat.F32.dual(),TypeFloat.F64.dual())); + for( double value : new double[]{-0.0,0.0,-1.0,1.0,3.0,16777216.0,16777217.0, + Double.NaN,Double.NEGATIVE_INFINITY,Double.POSITIVE_INFINITY} ) + inputs.add(TypeFloat.constant(value)); + // Isolated analysis inputs; no peepholes can fold the tested nodes. + Node lhs = new ConstantNode(Type.BOTTOM); + Node rhs = new ConstantNode(Type.BOTTOM); + for( Node op : new Node[]{new AddFNode(lhs,rhs),new SubFNode(lhs,rhs), + new MulFNode(lhs,rhs),new DivFNode(lhs,rhs)} ) { + Type[][] results = new Type[inputs.size()][inputs.size()]; + for( int i = 0; i<inputs.size(); i++ ) + for( int j = 0; j<inputs.size(); j++ ) { + lhs._type = inputs.get(i); + rhs._type = inputs.get(j); + results[i][j] = op.compute(); + } + for( int i = 0; i<inputs.size(); i++ ) + for( int j = 0; j<inputs.size(); j++ ) + if( inputs.get(i).isa(inputs.get(j)) ) + for( int k = 0; k<inputs.size(); k++ ) + if( !results[i][k].isa(results[j][k]) || !results[k][i].isa(results[k][j]) ) + fail(op.label()+": "+inputs.get(i)+" -> "+inputs.get(j)+ + ", other="+inputs.get(k)+"; lhs results "+results[i][k]+" -> "+results[j][k]+ + "; rhs results "+results[k][i]+" -> "+results[k][j]); + } + } +} |
