diff --git a/cobol-parser/src/main/scala/za/co/absa/cobrix/cobol/parser/decoders/FloatingPointDecoders.scala b/cobol-parser/src/main/scala/za/co/absa/cobrix/cobol/parser/decoders/FloatingPointDecoders.scala index 2972c18f..71187e4d 100644 --- a/cobol-parser/src/main/scala/za/co/absa/cobrix/cobol/parser/decoders/FloatingPointDecoders.scala +++ b/cobol-parser/src/main/scala/za/co/absa/cobrix/cobol/parser/decoders/FloatingPointDecoders.scala @@ -128,7 +128,7 @@ object FloatingPointDecoders { def decodeIbmSingleBigEndian(bytes: Array[Byte]): java.lang.Float = { try { val IBM32_SIGN_MASK = 0x80000000 - val IBM32_EXPONENT_MASK = 0x80000000 + val IBM32_EXPONENT_MASK = 0x7F000000 val IBM32_FRACTURE_MASK = 0x00FFFFFF val IBM32_MS_NIBBLE = 0x00F00000 @@ -148,7 +148,7 @@ object FloatingPointDecoders { } val leadingZeros = ((BIT_COUNT_MAGIC >> (topNibble >> 19)) & 3).toInt fracture <<= leadingZeros - val convertedExp = exponent + 131 - leadingZeros + val convertedExp = exponent - 131 - leadingZeros if (convertedExp >=0 && convertedExp < 254) { val ieee754Int = sign + (convertedExp << 23) + fracture diff --git a/cobol-parser/src/test/scala/za/co/absa/cobrix/cobol/parser/decoders/FloatingPointDecodersSpec.scala b/cobol-parser/src/test/scala/za/co/absa/cobrix/cobol/parser/decoders/FloatingPointDecodersSpec.scala index f9e5ab9b..96db5738 100644 --- a/cobol-parser/src/test/scala/za/co/absa/cobrix/cobol/parser/decoders/FloatingPointDecodersSpec.scala +++ b/cobol-parser/src/test/scala/za/co/absa/cobrix/cobol/parser/decoders/FloatingPointDecodersSpec.scala @@ -29,10 +29,52 @@ class FloatingPointDecodersSpec extends AnyWordSpec { } "decodeIbmSingleBigEndian()" should { - "decode IBM single precision / big-endian FP numbers" in { + "decode IBM single precision / big-endian FP number 1.0" in { val bytes = Array[Byte]( - 0x43.toByte, 0x14.toByte, 0x2E.toByte, 0xFC.toByte) - assertFloatEqual(FloatingPointDecoders.decodeIbmSingleBigEndian(bytes), 5.045883f) + 0x41.toByte, 0x10.toByte, 0x00.toByte, 0x00.toByte) + assertFloatEqual(FloatingPointDecoders.decodeIbmSingleBigEndian(bytes), 1.0f) + } + + "decode IBM single precision / big-endian FP number 1234.0" in { + val bytes = Array[Byte]( + 0x43.toByte, 0x4D.toByte, 0x20.toByte, 0x00.toByte) + assertFloatEqual(FloatingPointDecoders.decodeIbmSingleBigEndian(bytes), 1234.0f) + } + + "decode IBM single precision / big-endian FP number -1234.0" in { + val bytes = Array[Byte]( + 0xC3.toByte, 0x4D.toByte, 0x20.toByte, 0x00.toByte) + assertFloatEqual(FloatingPointDecoders.decodeIbmSingleBigEndian(bytes), -1234.0f) + } + + "decode IBM single precision / big-endian FP number 4.5" in { + val bytes = Array[Byte]( + 0x41.toByte, 0x48.toByte, 0x00.toByte, 0x00.toByte) + assertFloatEqual(FloatingPointDecoders.decodeIbmSingleBigEndian(bytes), 4.5f) + } + + "decode IBM single precision / big-endian FP number -3.75" in { + val bytes = Array[Byte]( + 0xC1.toByte, 0x3C.toByte, 0x00.toByte, 0x00.toByte) + assertFloatEqual(FloatingPointDecoders.decodeIbmSingleBigEndian(bytes), -3.75f) + } + + "decode IBM single precision / big-endian FP number 2.5" in { + val bytes = Array[Byte]( + 0x41.toByte, 0x28.toByte, 0x00.toByte, 0x00.toByte) + assertFloatEqual(FloatingPointDecoders.decodeIbmSingleBigEndian(bytes), 2.5f) + } + + "decode IBM single precision / big-endian FP number 0" in { + val bytes = Array[Byte]( + 0x00.toByte, 0x00.toByte, 0x00.toByte, 0x00.toByte) + assertFloatEqual(FloatingPointDecoders.decodeIbmSingleBigEndian(bytes), 0f) + } + + "decode IBM single precision / big-endian FP number infinity" in { + val bytes = Array[Byte]( + 0x7F.toByte, 0xFF.toByte, 0xFF.toByte, 0xFF.toByte) + assert(FloatingPointDecoders.decodeIbmSingleBigEndian(bytes).isInfinite) } } @@ -53,10 +95,40 @@ class FloatingPointDecodersSpec extends AnyWordSpec { } "decodeIbmSingleLittleEndian()" should { - "decode IBM single precision / little-endian FP numbers" in { + "decode IBM single precision / little-endian FP number 1.0" in { val bytes = Array[Byte]( - 0xFC.toByte, 0x2E.toByte, 0x14.toByte, 0x43.toByte) - assertFloatEqual(FloatingPointDecoders.decodeIbmSingleLittleEndian(bytes), 5.045883f) + 0x00.toByte, 0x00.toByte, 0x10.toByte, 0x41.toByte) + assertFloatEqual(FloatingPointDecoders.decodeIbmSingleLittleEndian(bytes), 1.0f) + } + + "decode IBM single precision / little-endian FP number 1234.0" in { + val bytes = Array[Byte]( + 0x00.toByte, 0x20.toByte, 0x4D.toByte, 0x43.toByte) + assertFloatEqual(FloatingPointDecoders.decodeIbmSingleLittleEndian(bytes), 1234.0f) + } + + "decode IBM single precision / little-endian FP number -1234.0" in { + val bytes = Array[Byte]( + 0x00.toByte, 0x20.toByte, 0x4D.toByte, 0xC3.toByte) + assertFloatEqual(FloatingPointDecoders.decodeIbmSingleLittleEndian(bytes), -1234.0f) + } + + "decode IBM single precision / little-endian FP number 4.5" in { + val bytes = Array[Byte]( + 0x00.toByte, 0x00.toByte, 0x48.toByte, 0x41.toByte) + assertFloatEqual(FloatingPointDecoders.decodeIbmSingleLittleEndian(bytes), 4.5f) + } + + "decode IBM single precision / little-endian FP number -3.75" in { + val bytes = Array[Byte]( + 0x00.toByte, 0x00.toByte, 0x3C.toByte, 0xC1.toByte) + assertFloatEqual(FloatingPointDecoders.decodeIbmSingleLittleEndian(bytes), -3.75f) + } + + "decode IBM single precision / little-endian FP number infinity" in { + val bytes = Array[Byte]( + 0xFF.toByte, 0xFF.toByte, 0xFF.toByte, 0x7F.toByte) + assert(FloatingPointDecoders.decodeIbmSingleLittleEndian(bytes).isInfinite) } } diff --git a/spark-cobol/src/test/scala/za/co/absa/cobrix/spark/cobol/source/regression/Test03IbmFloats.scala b/spark-cobol/src/test/scala/za/co/absa/cobrix/spark/cobol/source/regression/Test03IbmFloats.scala index fc57c6c1..7d5fe747 100644 --- a/spark-cobol/src/test/scala/za/co/absa/cobrix/spark/cobol/source/regression/Test03IbmFloats.scala +++ b/spark-cobol/src/test/scala/za/co/absa/cobrix/spark/cobol/source/regression/Test03IbmFloats.scala @@ -114,7 +114,7 @@ class Test03IbmFloats extends AnyFunSuite with BeforeAndAfterAll with SparkTestB val f = df.select(col("F")).take(1)(0)(0).asInstanceOf[Float] val d = df.select(col("D")).take(1)(0)(0).asInstanceOf[Double] - assertFloatEqual(f, 5.045883f) + assertFloatEqual(f, 322.93652f) assertDoubleEqual(d, 322.936717) } @@ -132,7 +132,7 @@ class Test03IbmFloats extends AnyFunSuite with BeforeAndAfterAll with SparkTestB val f = df.select(col("F")).take(1)(0)(0).asInstanceOf[Float] val d = df.select(col("D")).take(1)(0)(0).asInstanceOf[Double] - assertFloatEqual(f, 5.045883f) + assertFloatEqual(f, 322.93652f) assertDoubleEqual(d, 322.936717) }