This is an automated email from the ASF dual-hosted git repository.

curth pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-dotnet.git


The following commit(s) were added to refs/heads/main by this push:
     new 009762e  Improvements to decimal conversion (#292)
009762e is described below

commit 009762ed78eab160c1b31774e4db6480a5ae5f80
Author: Curt Hagenlocher <[email protected]>
AuthorDate: Wed Mar 25 10:46:44 2026 -0700

    Improvements to decimal conversion (#292)
    
    ## What's Changed
    
    - Fixes DecimalUtility.GetDecimal to correctly handle high-scale values
    (e.g., Decimal256 with precision 76, scale 38) that previously threw
    OverflowException — addresses issue #247
    - Adds a fast path using Int128 on .NET 7+ that avoids BigInteger
    allocation for values that fit in 128 bits
    - Replaces the old fractional-part overflow check (which threw) with
    FractionToDecimal, which gracefully reduces precision to fit within
    decimal's ~28-digit mantissa
    
    Closes #247.
    Partially addresses #96.
---
 .gitignore                                         |   2 +
 src/Apache.Arrow/Arrays/Decimal128Array.cs         |  34 +++++
 src/Apache.Arrow/Arrays/Decimal256Array.cs         |  33 +++++
 src/Apache.Arrow/DecimalUtility.cs                 | 159 +++++++++++++++++++--
 .../DecimalArrayBenchmark.cs                       | 131 +++++++++++++++++
 test/Apache.Arrow.Tests/Decimal128ArrayTests.cs    |  77 ++++++++++
 test/Apache.Arrow.Tests/Decimal256ArrayTests.cs    |  97 +++++++++++++
 test/Apache.Arrow.Tests/DecimalUtilityTests.cs     |   2 +-
 8 files changed, 521 insertions(+), 14 deletions(-)

diff --git a/.gitignore b/.gitignore
index 0ec0753..f0bff0c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -282,3 +282,5 @@ artifacts/
 
 # add .sln files back because they are ignored by the root .gitignore file
 !*.sln
+
+BenchmarkDotNet.Artifacts/
diff --git a/src/Apache.Arrow/Arrays/Decimal128Array.cs 
b/src/Apache.Arrow/Arrays/Decimal128Array.cs
index 2fb6f4c..15924d7 100644
--- a/src/Apache.Arrow/Arrays/Decimal128Array.cs
+++ b/src/Apache.Arrow/Arrays/Decimal128Array.cs
@@ -138,6 +138,11 @@ namespace Apache.Arrow
         public int Precision => ((Decimal128Type)Data.DataType).Precision;
         public int ByteWidth => ((Decimal128Type)Data.DataType).ByteWidth;
 
+        /// <summary>
+        /// Gets the decimal value at the index of the array. May throw an 
exception if the value can't be
+        /// expressed as a <see cref="System.Decimal "/>. See <see 
cref="TryGetValue(int, out decimal?)" /> for
+        /// details.
+        /// </summary>
         public decimal? GetValue(int index)
         {
             if (IsNull(index))
@@ -147,6 +152,35 @@ namespace Apache.Arrow
             return DecimalUtility.GetDecimal(ValueBuffer, Offset + index, 
Scale, ByteWidth);
         }
 
+        /// <summary>
+        /// Gets the decimal value at the index of the array. Returns false if 
the value can't be
+        /// expressed as a <see cref="System.Decimal "/>. <see 
cref="System.Decimal "/> is a 128-bit
+        /// floating point value with a 5 bit base-10 exponent and a 96-bit 
base-10 mantissa. Decimal128
+        /// is a fixed point 128 bit value where up to 128 bits can be used 
for the mantissa, and both
+        /// the number of bits and the exponent are determined by the array's 
type (which is out-of-band).
+        /// This means that a <see cref="Decimal128Type"/> whose precision 
minus scale is greater than 28
+        /// might produce an overflow when stored as a .NET decimal. It may 
also cause rounding for
+        /// precisions greater than 28. These will silently succeed. By 
contrast, <see cref="SqlDecimal"/>
+        /// can store a decimal128 value with full fidelity.
+        /// </summary>
+        public bool TryGetValue(int index, out decimal? value)
+        {
+            if (IsNull(index))
+            {
+                value = null;
+                return true;
+            }
+
+            if (DecimalUtility.TryGetDecimal(ValueBuffer, Offset + index, 
Scale, ByteWidth, out decimal result))
+            {
+                value = result;
+                return true;
+            }
+
+            value = null;
+            return false;
+        }
+
         public IList<decimal?> ToList(bool includeNulls = false)
         {
             var list = new List<decimal?>(Length);
diff --git a/src/Apache.Arrow/Arrays/Decimal256Array.cs 
b/src/Apache.Arrow/Arrays/Decimal256Array.cs
index 52bfb9e..39b36c1 100644
--- a/src/Apache.Arrow/Arrays/Decimal256Array.cs
+++ b/src/Apache.Arrow/Arrays/Decimal256Array.cs
@@ -146,6 +146,11 @@ namespace Apache.Arrow
         public int Precision => ((Decimal256Type)Data.DataType).Precision;
         public int ByteWidth => ((Decimal256Type)Data.DataType).ByteWidth;
 
+        /// <summary>
+        /// Gets the decimal value at the index of the array. May throw an 
exception if the value can't be
+        /// expressed as a <see cref="System.Decimal "/>. See <see 
cref="TryGetValue(int, out decimal?)" /> for
+        /// details.
+        /// </summary>
         public decimal? GetValue(int index)
         {
             if (IsNull(index))
@@ -156,6 +161,34 @@ namespace Apache.Arrow
             return DecimalUtility.GetDecimal(ValueBuffer, Offset + index, 
Scale, ByteWidth);
         }
 
+        /// <summary>
+        /// Gets the decimal value at the index of the array. Returns false if 
the value can't be
+        /// expressed as a <see cref="System.Decimal "/>. <see 
cref="System.Decimal "/> is a 128-bit
+        /// floating point value with a 5 bit base-10 exponent and a 96-bit 
base-10 mantissa. Decimal256
+        /// is a fixed point 256 bit value where up to 256 bits can be used 
for the mantissa, and both
+        /// the number of bits and the exponent are determined by the array's 
type (which is out-of-band).
+        /// This means that a <see cref="Decimal256Type"/> whose precision 
minus scale is greater than 28
+        /// might produce an overflow when stored as a .NET decimal. It may 
also cause rounding for
+        /// precisions greater than 28. These will silently succeed.
+        /// </summary>
+        public bool TryGetValue(int index, out decimal? value)
+        {
+            if (IsNull(index))
+            {
+                value = null;
+                return true;
+            }
+
+            if (DecimalUtility.TryGetDecimal(ValueBuffer, Offset + index, 
Scale, ByteWidth, out decimal result))
+            {
+                value = result;
+                return true;
+            }
+
+            value = null;
+            return false;
+        }
+
         public IList<decimal?> ToList(bool includeNulls = false)
         {
             var list = new List<decimal?>(Length);
diff --git a/src/Apache.Arrow/DecimalUtility.cs 
b/src/Apache.Arrow/DecimalUtility.cs
index c4e7256..968894a 100644
--- a/src/Apache.Arrow/DecimalUtility.cs
+++ b/src/Apache.Arrow/DecimalUtility.cs
@@ -14,6 +14,9 @@
 // limitations under the License.
 
 using System;
+#if NET7_0_OR_GREATER
+using System.Buffers.Binary;
+#endif
 using System.Data.SqlTypes;
 using System.Numerics;
 
@@ -35,10 +38,131 @@ namespace Apache.Arrow
 
         private static int PowersOfTenLength => s_powersOfTen.Length - 1;
 
+#if NET7_0_OR_GREATER
+        // decimal mantissa is 96 bits unsigned
+        private static readonly UInt128 s_maxDecimalMantissa = new 
UInt128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF);
+
+        private static readonly UInt128[] s_uint128PowersOfTen = 
ComputeUInt128Powers();
+
+        private static UInt128[] ComputeUInt128Powers()
+        {
+            var powers = new UInt128[39]; // 10^0 through 10^38
+            powers[0] = 1;
+            for (int i = 1; i < powers.Length; i++)
+                powers[i] = powers[i - 1] * 10;
+            return powers;
+        }
+#endif
+
         internal static decimal GetDecimal(in ArrowBuffer valueBuffer, int 
index, int scale, int byteWidth)
+        {
+            if (!TryGetDecimal(valueBuffer, index, scale, byteWidth, out 
decimal result))
+            {
+                throw new OverflowException("Value is too large or too small 
to be represented as a decimal");
+            }
+            return result;
+        }
+
+        internal static bool TryGetDecimal(in ArrowBuffer valueBuffer, int 
index, int scale, int byteWidth, out decimal result)
         {
             int startIndex = index * byteWidth;
             ReadOnlySpan<byte> value = valueBuffer.Span.Slice(startIndex, 
byteWidth);
+
+#if NET7_0_OR_GREATER
+            if (byteWidth == 16)
+            {
+                Int128 int128Value = 
BinaryPrimitives.ReadInt128LittleEndian(value);
+                return TryGetDecimalViaInt128(int128Value, scale, out result);
+            }
+
+            if (byteWidth == 32)
+            {
+                // Check if the value fits in 128 bits (upper 128 bits are 
sign extension)
+                Int128 lower = BinaryPrimitives.ReadInt128LittleEndian(value);
+                Int128 upper = 
BinaryPrimitives.ReadInt128LittleEndian(value.Slice(16));
+                Int128 signExtension = lower < 0 ? Int128.NegativeOne : 
Int128.Zero;
+                if (upper == signExtension)
+                {
+                    return TryGetDecimalViaInt128(lower, scale, out result);
+                }
+            }
+#endif
+
+            return TryGetDecimalViaBigInteger(value, scale, out result);
+        }
+
+#if NET7_0_OR_GREATER
+        private static bool TryGetDecimalViaInt128(Int128 integerValue, int 
scale, out decimal result)
+        {
+            bool negative = integerValue < 0;
+            UInt128 abs = negative ? (UInt128)(-integerValue) : 
(UInt128)integerValue;
+
+            // Fast path: value fits directly in decimal (96-bit mantissa, 
scale <= 28)
+            if (abs <= s_maxDecimalMantissa && scale <= 28)
+            {
+                result = UInt128ToDecimal(abs, negative, (byte)scale);
+                return true;
+            }
+
+            if (scale == 0)
+            {
+                result = default;
+                return false;
+            }
+
+            // Split into integer and fractional parts
+            if (scale <= 38)
+            {
+                UInt128 scaleBy = s_uint128PowersOfTen[scale];
+                (UInt128 intPart, UInt128 fracPart) = UInt128.DivRem(abs, 
scaleBy);
+
+                if (intPart > s_maxDecimalMantissa)
+                {
+                    result = default;
+                    return false;
+                }
+
+                decimal intDecimal = UInt128ToDecimal(intPart, negative, 0);
+
+                // Reduce fractional part to fit decimal constraints
+                int fracScale = scale;
+                while (fracPart > s_maxDecimalMantissa || fracScale > 28)
+                {
+                    fracPart /= 10;
+                    fracScale--;
+                }
+
+                decimal fracDecimal = UInt128ToDecimal(fracPart, false, 
(byte)fracScale);
+                result = negative ? intDecimal - fracDecimal : intDecimal + 
fracDecimal;
+                return true;
+            }
+            else
+            {
+                // scale > 38: abs < 2^127 < 10^39, so the integer part is 0 
or very small.
+                // Reduce mantissa and scale together until they fit decimal 
constraints.
+                UInt128 mantissa = abs;
+                int decScale = scale;
+                while (mantissa > s_maxDecimalMantissa || decScale > 28)
+                {
+                    mantissa /= 10;
+                    decScale--;
+                }
+
+                result = UInt128ToDecimal(mantissa, negative, (byte)decScale);
+                return true;
+            }
+        }
+
+        private static decimal UInt128ToDecimal(UInt128 value, bool negative, 
byte scale)
+        {
+            ulong lo64 = (ulong)(value & ulong.MaxValue);
+            uint hi32 = (uint)(value >> 64);
+            return new decimal((int)lo64, (int)(lo64 >> 32), (int)hi32, 
negative, scale);
+        }
+#endif
+
+        private static bool TryGetDecimalViaBigInteger(ReadOnlySpan<byte> 
value, int scale, out decimal result)
+        {
             BigInteger integerValue;
 
 #if NETCOREAPP
@@ -52,25 +176,19 @@ namespace Apache.Arrow
                 BigInteger scaleBy = BigInteger.Pow(10, scale);
                 BigInteger integerPart = BigInteger.DivRem(integerValue, 
scaleBy, out BigInteger fractionalPart);
 
-                // decimal overflow, not much we can do here - C# needs a 
BigDecimal
-                if (integerPart > _maxDecimal)
-                {
-                    throw new OverflowException($"Value: {integerPart} of 
{integerValue} is too big be represented as a decimal");
-                }
-                else if (integerPart < _minDecimal)
+                if (integerPart > _maxDecimal || integerPart < _minDecimal)
                 {
-                    throw new OverflowException($"Value: {integerPart} of 
{integerValue} is too small be represented as a decimal");
-                }
-                else if (fractionalPart > _maxDecimal || fractionalPart < 
_minDecimal)
-                {
-                    throw new OverflowException($"Value: {fractionalPart} of 
{integerValue} is too precise be represented as a decimal");
+                    result = default;
+                    return false;
                 }
 
-                return (decimal)integerPart + DivideByScale(fractionalPart, 
scale);
+                result = (decimal)integerPart + 
FractionToDecimal(fractionalPart, scale);
+                return true;
             }
             else
             {
-                return DivideByScale(integerValue, scale);
+                result = DivideByScale(integerValue, scale);
+                return true;
             }
         }
 
@@ -205,6 +323,21 @@ namespace Apache.Arrow
             }
         }
 
+        private static decimal FractionToDecimal(BigInteger fractionalPart, 
int scale)
+        {
+            // The fractional BigInteger may have more digits than decimal can 
represent (~28-29).
+            // Reduce it by dividing out powers of 10, losing the 
least-significant digits,
+            // then divide the remainder by the reduced scale.
+            int digitsRemoved = 0;
+            while (fractionalPart > _maxDecimal || fractionalPart < 
_minDecimal)
+            {
+                fractionalPart /= 10;
+                digitsRemoved++;
+            }
+
+            return DivideByScale(fractionalPart, scale - digitsRemoved);
+        }
+
         private static decimal DivideByScale(BigInteger integerValue, int 
scale)
         {
             decimal result = (decimal)integerValue; // this cast is safe here
diff --git a/test/Apache.Arrow.Benchmarks/DecimalArrayBenchmark.cs 
b/test/Apache.Arrow.Benchmarks/DecimalArrayBenchmark.cs
new file mode 100644
index 0000000..a3089a1
--- /dev/null
+++ b/test/Apache.Arrow.Benchmarks/DecimalArrayBenchmark.cs
@@ -0,0 +1,131 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Data.SqlTypes;
+using Apache.Arrow.Types;
+using BenchmarkDotNet.Attributes;
+
+namespace Apache.Arrow.Benchmarks
+{
+    [MemoryDiagnoser]
+    public class DecimalArrayBenchmark
+    {
+        [Params(10_000)]
+        public int Count { get; set; }
+
+        private Decimal128Array _decimal128LowScale;
+        private Decimal128Array _decimal128HighScale;
+        private Decimal256Array _decimal256LowScale;
+        private Decimal256Array _decimal256HighScale;
+
+        [GlobalSetup]
+        public void GlobalSetup()
+        {
+            var random = new Random(42);
+
+            _decimal128LowScale = BuildDecimal128Array(new Decimal128Type(14, 
4), random);
+            _decimal128HighScale = BuildDecimal128Array(new Decimal128Type(38, 
20), random);
+            _decimal256LowScale = BuildDecimal256Array(new Decimal256Type(14, 
4), random);
+            _decimal256HighScale = BuildDecimal256Array(new Decimal256Type(76, 
38), random);
+        }
+
+        private Decimal128Array BuildDecimal128Array(Decimal128Type type, 
Random random)
+        {
+            var builder = new Decimal128Array.Builder(type);
+            for (int i = 0; i < Count; i++)
+            {
+                builder.Append((decimal)Math.Round(random.NextDouble() * 
10000, Math.Min(type.Scale, 10)));
+            }
+            return builder.Build();
+        }
+
+        private Decimal256Array BuildDecimal256Array(Decimal256Type type, 
Random random)
+        {
+            var builder = new Decimal256Array.Builder(type);
+            for (int i = 0; i < Count; i++)
+            {
+                builder.Append((decimal)Math.Round(random.NextDouble() * 
10000, Math.Min(type.Scale, 10)));
+            }
+            return builder.Build();
+        }
+
+        [Benchmark]
+        public decimal? Decimal128_GetValue_LowScale()
+        {
+            decimal? sum = 0;
+            for (int i = 0; i < _decimal128LowScale.Length; i++)
+            {
+                sum += _decimal128LowScale.GetValue(i);
+            }
+            return sum;
+        }
+
+        [Benchmark]
+        public decimal? Decimal128_GetValue_HighScale()
+        {
+            decimal? sum = 0;
+            for (int i = 0; i < _decimal128HighScale.Length; i++)
+            {
+                sum += _decimal128HighScale.GetValue(i);
+            }
+            return sum;
+        }
+
+        [Benchmark]
+        public decimal? Decimal256_GetValue_LowScale()
+        {
+            decimal? sum = 0;
+            for (int i = 0; i < _decimal256LowScale.Length; i++)
+            {
+                sum += _decimal256LowScale.GetValue(i);
+            }
+            return sum;
+        }
+
+        [Benchmark]
+        public decimal? Decimal256_GetValue_HighScale()
+        {
+            decimal? sum = 0;
+            for (int i = 0; i < _decimal256HighScale.Length; i++)
+            {
+                sum += _decimal256HighScale.GetValue(i);
+            }
+            return sum;
+        }
+
+        [Benchmark]
+        public SqlDecimal? Decimal128_GetSqlDecimal()
+        {
+            SqlDecimal? sum = 0;
+            for (int i = 0; i < _decimal128LowScale.Length; i++)
+            {
+                sum += _decimal128LowScale.GetSqlDecimal(i);
+            }
+            return sum;
+        }
+
+        [Benchmark]
+        public string Decimal256_GetString_HighScale()
+        {
+            string last = null;
+            for (int i = 0; i < _decimal256HighScale.Length; i++)
+            {
+                last = _decimal256HighScale.GetString(i);
+            }
+            return last;
+        }
+    }
+}
diff --git a/test/Apache.Arrow.Tests/Decimal128ArrayTests.cs 
b/test/Apache.Arrow.Tests/Decimal128ArrayTests.cs
index c5e0647..0ea6326 100644
--- a/test/Apache.Arrow.Tests/Decimal128ArrayTests.cs
+++ b/test/Apache.Arrow.Tests/Decimal128ArrayTests.cs
@@ -459,6 +459,83 @@ namespace Apache.Arrow.Tests
             }
         }
 
+        public class HighPrecisionGetValue
+        {
+            [Fact]
+            public void GetValueWithHighScale()
+            {
+                // Decimal128 supports up to precision 38, scale 38
+                var array = new Decimal128Array.Builder(new Decimal128Type(38, 
20))
+                    .Append(2422.85527600000m)
+                    .Build();
+
+                Assert.Equal(2422.85527600000m, array.GetValue(0));
+            }
+
+            [Fact]
+            public void GetValueWithMaxScaleFractionalOnly()
+            {
+                var array = new Decimal128Array.Builder(new Decimal128Type(38, 
30))
+                    .Append(0.12345678m)
+                    .Build();
+
+                Assert.Equal(0.12345678m, array.GetValue(0));
+            }
+
+            [Fact]
+            public void GetValueWithHighScaleNegative()
+            {
+                var array = new Decimal128Array.Builder(new Decimal128Type(38, 
20))
+                    .Append(-2422.85527600000m)
+                    .Build();
+
+                Assert.Equal(-2422.85527600000m, array.GetValue(0));
+            }
+
+            [Fact]
+            public void TryGetValueReturnsTrue()
+            {
+                var array = new Decimal128Array.Builder(new Decimal128Type(38, 
20))
+                    .Append(2422.85527600000m)
+                    .Build();
+
+                Assert.True(array.TryGetValue(0, out decimal? value));
+                Assert.Equal(2422.85527600000m, value);
+            }
+
+            [Fact]
+            public void TryGetValueReturnsFalse()
+            {
+                var array = new Decimal128Array.Builder(new Decimal128Type(38, 
4))
+                    
.Append(SqlDecimal.Parse("100000000000000000000000000000000"))
+                    .Build();
+
+                Assert.False(array.TryGetValue(0, out decimal? value));
+            }
+
+            [Fact]
+            public void TryGetValueCanRound()
+            {
+                var array = new Decimal128Array.Builder(new Decimal128Type(38, 
8))
+                    
.Append(SqlDecimal.Parse("10000000000000000000000000000.99"))
+                    .Build();
+
+                Assert.True(array.TryGetValue(0, out decimal? value));
+                Assert.Equal(10000000000000000000000000001m, value);
+            }
+
+            [Fact]
+            public void TryGetValueNullReturnsTrue()
+            {
+                var array = new Decimal128Array.Builder(new Decimal128Type(38, 
20))
+                    .AppendNull()
+                    .Build();
+
+                Assert.True(array.TryGetValue(0, out decimal? value));
+                Assert.Null(value);
+            }
+        }
+
         [Fact]
         public void SliceDecimal128Array()
         {
diff --git a/test/Apache.Arrow.Tests/Decimal256ArrayTests.cs 
b/test/Apache.Arrow.Tests/Decimal256ArrayTests.cs
index 1c94916..a604edf 100644
--- a/test/Apache.Arrow.Tests/Decimal256ArrayTests.cs
+++ b/test/Apache.Arrow.Tests/Decimal256ArrayTests.cs
@@ -477,6 +477,103 @@ namespace Apache.Arrow.Tests
             }
         }
 
+        public class HighPrecisionGetValue
+        {
+            [Fact]
+            public void GetValueWithHighPrecisionAndScale()
+            {
+                // Exact scenario from 
https://github.com/apache/arrow-dotnet/issues/247
+                var array = new Decimal256Array.Builder(new Decimal256Type(76, 
38))
+                    .Append(2422.85527600000m)
+                    .Build();
+
+                Assert.Equal(2422.85527600000m, array.GetValue(0));
+            }
+
+            [Fact]
+            public void GetValueWithHighScaleFractionalOnly()
+            {
+                var array = new Decimal256Array.Builder(new Decimal256Type(76, 
38))
+                    .Append(0.12345678901234567890m)
+                    .Build();
+
+                Assert.Equal(0.12345678901234567890m, array.GetValue(0));
+            }
+
+            [Fact]
+            public void GetValueWithHighScaleNegative()
+            {
+                var array = new Decimal256Array.Builder(new Decimal256Type(76, 
38))
+                    .Append(-2422.85527600000m)
+                    .Build();
+
+                Assert.Equal(-2422.85527600000m, array.GetValue(0));
+            }
+
+            [Fact]
+            public void GetValueWithHighScaleZero()
+            {
+                var array = new Decimal256Array.Builder(new Decimal256Type(76, 
38))
+                    .Append(0m)
+                    .Build();
+
+                Assert.Equal(0m, array.GetValue(0));
+            }
+
+            [Fact]
+            public void GetValueWithHighScaleWholeNumber()
+            {
+                var array = new Decimal256Array.Builder(new Decimal256Type(76, 
38))
+                    .Append(12345m)
+                    .Build();
+
+                Assert.Equal(12345m, array.GetValue(0));
+            }
+
+            [Fact]
+            public void TryGetValueReturnsTrue()
+            {
+                var array = new Decimal256Array.Builder(new Decimal256Type(76, 
38))
+                    .Append(2422.85527600000m)
+                    .Build();
+
+                Assert.True(array.TryGetValue(0, out decimal? value));
+                Assert.Equal(2422.85527600000m, value);
+            }
+
+            [Fact]
+            public void TryGetValueReturnsFalse()
+            {
+                var array = new Decimal256Array.Builder(new Decimal256Type(38, 
4))
+                    
.Append(SqlDecimal.Parse("100000000000000000000000000000000"))
+                    .Build();
+
+                Assert.False(array.TryGetValue(0, out decimal? value));
+            }
+
+            [Fact]
+            public void TryGetValueCanRound()
+            {
+                var array = new Decimal256Array.Builder(new Decimal256Type(38, 
8))
+                    
.Append(SqlDecimal.Parse("10000000000000000000000000000.99"))
+                    .Build();
+
+                Assert.True(array.TryGetValue(0, out decimal? value));
+                Assert.Equal(10000000000000000000000000001m, value);
+            }
+
+            [Fact]
+            public void TryGetValueNullReturnsTrue()
+            {
+                var array = new Decimal256Array.Builder(new Decimal256Type(76, 
38))
+                    .AppendNull()
+                    .Build();
+
+                Assert.True(array.TryGetValue(0, out decimal? value));
+                Assert.Null(value);
+            }
+        }
+
         [Fact]
         public void SliceDecimal256Array()
         {
diff --git a/test/Apache.Arrow.Tests/DecimalUtilityTests.cs 
b/test/Apache.Arrow.Tests/DecimalUtilityTests.cs
index 1156ecb..7f2b3ef 100644
--- a/test/Apache.Arrow.Tests/DecimalUtilityTests.cs
+++ b/test/Apache.Arrow.Tests/DecimalUtilityTests.cs
@@ -50,7 +50,7 @@ namespace Apache.Arrow.Tests
 
             [Theory]
             [InlineData(4.56, 38, 9, false)]
-            [InlineData(7.89, 76, 38, true)]
+            [InlineData(7.89, 76, 38, false)]
             public void Decimal256HasExpectedResultOrThrows(decimal d, int 
precision, int scale, bool shouldThrow)
             {
                 var builder = new Decimal256Array.Builder(new 
Decimal256Type(precision, scale));

Reply via email to