Skip to content

Commit ddc86b8

Browse files
copilot-theorem: Correctly display special Float values in counterexamples. Refs #697.
`copilot-theorem`'s counterexample-reporting machinery up-casts `Float` values to `Double` values using the `realToFrac` function. `realToFrac` incorrectly handles special floating-point values such as negative zero, infinity, and NaN values, causing `copilot-theorem` to generate incorrect counterexamples when these special values are involved. This commit removes the use of `realToFrac` in favor of an alternative approach based on `GHC.Float.double2Float`, which correctly handles most special floating-point values. A notable exception is NaN values, as `double2Float` does not reliably preserve the payload of a NaN value. As such, we include a special case for NaN values that takes care to preserve payloads. Co-authored-by: Ryan Scott <rscott@galois.com>
1 parent aaca8d1 commit ddc86b8

2 files changed

Lines changed: 23 additions & 2 deletions

File tree

copilot-theorem/copilot-theorem.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ library
5151
, containers >= 0.4 && < 0.9
5252
, data-default >= 0.7 && < 0.9
5353
, directory >= 1.3 && < 1.4
54+
, fp-ieee >= 0.1 && < 0.2
5455
, libBF >= 0.6.2 && < 0.7
5556
, mtl >= 2.0 && < 2.4
5657
, panic >= 0.4.0 && < 0.5

copilot-theorem/src/Copilot/Theorem/What4.hs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,9 @@ import Data.Parameterized.NatRepr
7777
import Data.Parameterized.Nonce
7878
import Data.Parameterized.Some
7979
import qualified Data.Parameterized.Vector as V
80-
import GHC.Float (castWord32ToFloat, castWord64ToDouble)
80+
import GHC.Float (castWord32ToFloat, castWord64ToDouble, double2Float)
8181
import LibBF (BigFloat, bfToDouble, pattern NearEven)
82+
import Numeric.Floating.IEEE.NaN (getPayload, isSignaling, setPayload, setPayloadSignaling)
8283
import qualified Panic as Panic
8384

8485
import Copilot.Theorem.What4.Translate
@@ -707,7 +708,7 @@ valFromExpr ge xe = case xe of
707708
XFloat e ->
708709
Some . CopilotValue CT.Float <$>
709710
iFloatGroundEval WFP.SingleFloatRepr e
710-
(realToFrac . fst . bfToDouble NearEven)
711+
(nanSafeDoubleToFloat . fst . bfToDouble NearEven)
711712
fromRational
712713
(castWord32ToFloat . fromInteger . BV.asUnsigned)
713714
XDouble e ->
@@ -757,3 +758,22 @@ valFromExpr ge xe = case xe of
757758
WB.FloatIEEERepr -> ieeeK <$> WG.groundEval ge e
758759
WB.FloatRealRepr -> realK <$> WG.groundEval ge e
759760
WB.FloatUninterpretedRepr -> uninterpK <$> WG.groundEval ge e
761+
762+
-- Convert a Double to a Float. This function takes care to preserve
763+
-- special floating-point values, such as negative zero, infinity, and NaN
764+
-- values.
765+
nanSafeDoubleToFloat :: Double -> Float
766+
nanSafeDoubleToFloat x
767+
| isNaN x = convertDoubleNaNToFloatNan x
768+
| otherwise = double2Float x
769+
where
770+
payload :: Float
771+
payload = double2Float $ getPayload x
772+
773+
-- double2Float does not preserve the payloads of NaN values, so we
774+
-- include a special case for translating NaNs.
775+
convertDoubleNaNToFloatNan :: Double -> Float
776+
convertDoubleNaNToFloatNan nan =
777+
if isSignaling nan
778+
then setPayloadSignaling payload
779+
else setPayload payload

0 commit comments

Comments
 (0)