Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/libraries/System.Memory/ref/System.Memory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,8 @@ namespace System.Buffers.Binary
{
public static partial class BinaryPrimitives
{
public static System.Numerics.BFloat16 ReadBFloat16BigEndian(System.ReadOnlySpan<byte> source) { throw null; }
public static System.Numerics.BFloat16 ReadBFloat16LittleEndian(System.ReadOnlySpan<byte> source) { throw null; }
public static double ReadDoubleBigEndian(System.ReadOnlySpan<byte> source) { throw null; }
public static double ReadDoubleLittleEndian(System.ReadOnlySpan<byte> source) { throw null; }
public static System.Half ReadHalfBigEndian(System.ReadOnlySpan<byte> source) { throw null; }
Expand Down Expand Up @@ -643,6 +645,8 @@ public static void ReverseEndianness(System.ReadOnlySpan<UInt128> source, System
public static void ReverseEndianness(System.ReadOnlySpan<ulong> source, System.Span<ulong> destination) { }
[System.CLSCompliant(false)]
public static void ReverseEndianness(System.ReadOnlySpan<ushort> source, System.Span<ushort> destination) { }
public static bool TryReadBFloat16BigEndian(System.ReadOnlySpan<byte> source, out System.Numerics.BFloat16 value) { throw null; }
public static bool TryReadBFloat16LittleEndian(System.ReadOnlySpan<byte> source, out System.Numerics.BFloat16 value) { throw null; }
public static bool TryReadDoubleBigEndian(System.ReadOnlySpan<byte> source, out double value) { throw null; }
public static bool TryReadDoubleLittleEndian(System.ReadOnlySpan<byte> source, out double value) { throw null; }
public static bool TryReadHalfBigEndian(System.ReadOnlySpan<byte> source, out System.Half value) { throw null; }
Expand Down Expand Up @@ -679,6 +683,8 @@ public static void ReverseEndianness(System.ReadOnlySpan<ushort> source, System.
public static bool TryReadUIntPtrBigEndian(System.ReadOnlySpan<byte> source, out nuint value) { throw null; }
[System.CLSCompliantAttribute(false)]
public static bool TryReadUIntPtrLittleEndian(System.ReadOnlySpan<byte> source, out nuint value) { throw null; }
public static bool TryWriteBFloat16BigEndian(System.Span<byte> destination, System.Numerics.BFloat16 value) { throw null; }
public static bool TryWriteBFloat16LittleEndian(System.Span<byte> destination, System.Numerics.BFloat16 value) { throw null; }
public static bool TryWriteDoubleBigEndian(System.Span<byte> destination, double value) { throw null; }
public static bool TryWriteDoubleLittleEndian(System.Span<byte> destination, double value) { throw null; }
public static bool TryWriteHalfBigEndian(System.Span<byte> destination, System.Half value) { throw null; }
Expand Down Expand Up @@ -715,6 +721,8 @@ public static void ReverseEndianness(System.ReadOnlySpan<ushort> source, System.
public static bool TryWriteUIntPtrBigEndian(System.Span<byte> destination, nuint value) { throw null; }
[System.CLSCompliantAttribute(false)]
public static bool TryWriteUIntPtrLittleEndian(System.Span<byte> destination, nuint value) { throw null; }
public static void WriteBFloat16BigEndian(System.Span<byte> destination, System.Numerics.BFloat16 value) { }
public static void WriteBFloat16LittleEndian(System.Span<byte> destination, System.Numerics.BFloat16 value) { }
public static void WriteDoubleBigEndian(System.Span<byte> destination, double value) { }
public static void WriteDoubleLittleEndian(System.Span<byte> destination, double value) { }
public static void WriteHalfBigEndian(System.Span<byte> destination, System.Half value) { }
Expand Down
137 changes: 89 additions & 48 deletions src/libraries/System.Memory/tests/Binary/BinaryReaderUnitTests.cs

Large diffs are not rendered by default.

87 changes: 83 additions & 4 deletions src/libraries/System.Private.CoreLib/src/System/BitConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,34 @@ public static bool TryWriteBytes(Span<byte> destination, UInt128 value)
return true;
}

/// <summary>
/// Returns the specified <see cref="BFloat16"/> value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 2.</returns>
public static unsafe byte[] GetBytes(BFloat16 value)
{
byte[] bytes = new byte[sizeof(BFloat16)];
bool success = TryWriteBytes(bytes, value);
Debug.Assert(success);
return bytes;
}

/// <summary>
/// Converts a <see cref="BFloat16"/> value into a span of bytes.
/// </summary>
/// <param name="destination">When this method returns, the bytes representing the converted <see cref="BFloat16"/> value.</param>
/// <param name="value">The <see cref="BFloat16"/> value to convert.</param>
/// <returns><see langword="true"/> if the conversion was successful; <see langword="false"/> otherwise.</returns>
public static unsafe bool TryWriteBytes(Span<byte> destination, BFloat16 value)
{
if (destination.Length < sizeof(BFloat16))
return false;

Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value);
return true;
}

/// <summary>
/// Returns the specified half-precision floating point value as an array of bytes.
/// </summary>
Expand Down Expand Up @@ -693,6 +721,31 @@ public static UInt128 ToUInt128(ReadOnlySpan<byte> value)
return Unsafe.ReadUnaligned<UInt128>(ref MemoryMarshal.GetReference(value));
}

/// <summary>
/// Returns a <see cref="BFloat16"/> number converted from two bytes at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within <paramref name="value"/>.</param>
/// <returns>A <see cref="BFloat16"/> number signed integer formed by two bytes beginning at <paramref name="startIndex"/>.</returns>
/// <exception cref="ArgumentException"><paramref name="startIndex"/> equals the length of <paramref name="value"/> minus 1.</exception>
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="startIndex"/> is less than zero or greater than the length of <paramref name="value"/> minus 1.</exception>
public static BFloat16 ToBFloat16(byte[] value, int startIndex) => Int16BitsToBFloat16(ToInt16(value, startIndex));

/// <summary>
/// Converts a read-only byte span into a <see cref="BFloat16"/> value.
/// </summary>
/// <param name="value">A read-only span containing the bytes to convert.</param>
/// <returns>A <see cref="BFloat16"/> value representing the converted bytes.</returns>
/// <exception cref="ArgumentOutOfRangeException">The length of <paramref name="value"/> is less than 2.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe BFloat16 ToBFloat16(ReadOnlySpan<byte> value)
{
if (value.Length < sizeof(BFloat16))
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value);
return Unsafe.ReadUnaligned<BFloat16>(ref MemoryMarshal.GetReference(value));
}

/// <summary>
/// Returns a half-precision floating point number converted from two bytes at a specified position in a byte array.
/// </summary>
Expand Down Expand Up @@ -948,9 +1001,21 @@ public static bool ToBoolean(ReadOnlySpan<byte> value)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Half Int16BitsToHalf(short value) => new Half((ushort)(value));

internal static short BFloat16BitsToInt16(BFloat16 value) => (short)value._value;
/// <summary>
/// Converts the specified <see cref="BFloat16"/> number to a 16-bit signed integer.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>A 16-bit signed integer whose bits are identical to <paramref name="value"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static short BFloat16ToInt16Bits(BFloat16 value) => (short)value._value;

internal static BFloat16 Int16BitsToBFloat16(short value) => new BFloat16((ushort)(value));
/// <summary>
/// Converts the specified 16-bit signed integer to a <see cref="BFloat16"/> number.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>A <see cref="BFloat16"/> number whose bits are identical to <paramref name="value"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BFloat16 Int16BitsToBFloat16(short value) => new BFloat16((ushort)(value));

/// <summary>
/// Converts the specified double-precision floating point number to a 64-bit unsigned integer.
Expand Down Expand Up @@ -1006,8 +1071,22 @@ public static bool ToBoolean(ReadOnlySpan<byte> value)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Half UInt16BitsToHalf(ushort value) => new Half(value);

internal static ushort BFloat16BitsToUInt16(BFloat16 value) => value._value;
/// <summary>
/// Converts the specified <see cref="BFloat16"/> number to a 16-bit unsigned integer.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>A 16-bit unsigned integer whose bits are identical to <paramref name="value"/>.</returns>
[CLSCompliant(false)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ushort BFloat16ToUInt16Bits(BFloat16 value) => value._value;

internal static BFloat16 UInt16BitsToBFloat16(ushort value) => new BFloat16(value);
/// <summary>
/// Converts the specified 16-bit unsigned integer to a <see cref="BFloat16"/> number.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>A <see cref="BFloat16"/> number whose bits are identical to <paramref name="value"/>.</returns>
[CLSCompliant(false)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BFloat16 UInt16BitsToBFloat16(ushort value) => new BFloat16(value);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

Expand Down Expand Up @@ -42,6 +43,23 @@ public static Half ReadHalfBigEndian(ReadOnlySpan<byte> source)
MemoryMarshal.Read<Half>(source);
}

/// <summary>
/// Reads a <see cref="BFloat16" /> from the beginning of a read-only span of bytes, as big endian.
/// </summary>
/// <param name="source">The read-only span to read.</param>
/// <returns>The big endian value.</returns>
/// <remarks>Reads exactly 2 bytes from the beginning of the span.</remarks>
/// <exception cref="ArgumentOutOfRangeException">
/// <paramref name="source"/> is too small to contain a <see cref="BFloat16" />.
/// </exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BFloat16 ReadBFloat16BigEndian(ReadOnlySpan<byte> source)
{
return BitConverter.IsLittleEndian ?
BitConverter.Int16BitsToBFloat16(ReverseEndianness(MemoryMarshal.Read<short>(source))) :
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
BitConverter.Int16BitsToBFloat16(ReverseEndianness(MemoryMarshal.Read<short>(source))) :
BitConverter.UInt16BitsToBFloat16(ReverseEndianness(MemoryMarshal.Read<ushort>(source))) :

Should we using UInt16 everywhere to match the field type?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current changes are at least consistent with ReadHalfLittleEndian, which is implemented with Int16 even though it's field is UInt16.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I just duplicated everything from Half.

MemoryMarshal.Read<BFloat16>(source);
}

/// <summary>
/// Reads a <see cref="short" /> from the beginning of a read-only span of bytes, as big endian.
/// </summary>
Expand Down Expand Up @@ -278,6 +296,28 @@ public static bool TryReadHalfBigEndian(ReadOnlySpan<byte> source, out Half valu
return MemoryMarshal.TryRead(source, out value);
}

/// <summary>
/// Reads a <see cref="BFloat16" /> from the beginning of a read-only span of bytes, as big endian.
/// </summary>
/// <param name="source">The read-only span of bytes to read.</param>
/// <param name="value">When this method returns, contains the value read out of the read-only span of bytes, as big endian.</param>
/// <returns>
/// <see langword="true" /> if the span is large enough to contain a <see cref="BFloat16" />; otherwise, <see langword="false" />.
/// </returns>
/// <remarks>Reads exactly 2 bytes from the beginning of the span.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryReadBFloat16BigEndian(ReadOnlySpan<byte> source, out BFloat16 value)
{
if (BitConverter.IsLittleEndian)
{
bool success = MemoryMarshal.TryRead(source, out short tmp);
value = BitConverter.Int16BitsToBFloat16(ReverseEndianness(tmp));
return success;
}

return MemoryMarshal.TryRead(source, out value);
}

/// <summary>
/// Reads a <see cref="short" /> from the beginning of a read-only span of bytes, as big endian.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

Expand Down Expand Up @@ -42,6 +43,23 @@ public static Half ReadHalfLittleEndian(ReadOnlySpan<byte> source)
MemoryMarshal.Read<Half>(source);
}

/// <summary>
/// Reads a <see cref="BFloat16" /> from the beginning of a read-only span of bytes, as little endian.
/// </summary>
/// <param name="source">The read-only span to read.</param>
/// <returns>The little endian value.</returns>
/// <remarks>Reads exactly 2 bytes from the beginning of the span.</remarks>
/// <exception cref="ArgumentOutOfRangeException">
/// <paramref name="source"/> is too small to contain a <see cref="Half" />.
/// </exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BFloat16 ReadBFloat16LittleEndian(ReadOnlySpan<byte> source)
{
return !BitConverter.IsLittleEndian ?
BitConverter.Int16BitsToBFloat16(ReverseEndianness(MemoryMarshal.Read<short>(source))) :
MemoryMarshal.Read<BFloat16>(source);
}

/// <summary>
/// Reads a <see cref="short" /> from the beginning of a read-only span of bytes, as little endian.
/// </summary>
Expand Down Expand Up @@ -278,6 +296,28 @@ public static bool TryReadHalfLittleEndian(ReadOnlySpan<byte> source, out Half v
return MemoryMarshal.TryRead(source, out value);
}

/// <summary>
/// Reads a <see cref="BFloat16" /> from the beginning of a read-only span of bytes, as little endian.
/// </summary>
/// <param name="source">The read-only span of bytes to read.</param>
/// <param name="value">When this method returns, contains the value read out of the read-only span of bytes, as little endian.</param>
/// <returns>
/// <see langword="true" /> if the span is large enough to contain a <see cref="BFloat16" />; otherwise, <see langword="false" />.
/// </returns>
/// <remarks>Reads exactly 2 bytes from the beginning of the span.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryReadBFloat16LittleEndian(ReadOnlySpan<byte> source, out BFloat16 value)
{
if (!BitConverter.IsLittleEndian)
{
bool success = MemoryMarshal.TryRead(source, out short tmp);
value = BitConverter.Int16BitsToBFloat16(ReverseEndianness(tmp));
return success;
}

return MemoryMarshal.TryRead(source, out value);
}

/// <summary>
/// Reads a <see cref="short" /> from the beginning of a read-only span of bytes, as little endian.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

Expand Down Expand Up @@ -54,6 +55,29 @@ public static void WriteHalfBigEndian(Span<byte> destination, Half value)
}
}

/// <summary>
/// Writes a <see cref="BFloat16" /> into a span of bytes, as big endian.
/// </summary>
/// <param name="destination">The span of bytes where the value is to be written, as big endian.</param>
/// <param name="value">The value to write into the span of bytes.</param>
/// <remarks>Writes exactly 2 bytes to the beginning of the span.</remarks>
/// <exception cref="ArgumentOutOfRangeException">
/// <paramref name="destination" /> is too small to contain a <see cref="BFloat16" />.
/// </exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteBFloat16BigEndian(Span<byte> destination, BFloat16 value)
{
if (BitConverter.IsLittleEndian)
{
short tmp = ReverseEndianness(BitConverter.BFloat16ToInt16Bits(value));
MemoryMarshal.Write(destination, in tmp);
}
else
{
MemoryMarshal.Write(destination, in value);
}
}

/// <summary>
/// Writes a <see cref="short" /> into a span of bytes, as big endian.
/// </summary>
Expand Down Expand Up @@ -354,6 +378,27 @@ public static bool TryWriteHalfBigEndian(Span<byte> destination, Half value)
return MemoryMarshal.TryWrite(destination, in value);
}

/// <summary>
/// Writes a <see cref="BFloat16" /> into a span of bytes, as big endian.
/// </summary>
/// <param name="destination">The span of bytes where the value is to be written, as big endian.</param>
/// <param name="value">The value to write into the span of bytes.</param>
/// <returns>
/// <see langword="true" /> if the span is large enough to contain a <see cref="BFloat16" />; otherwise, <see langword="false" />.
/// </returns>
/// <remarks>Writes exactly 2 bytes to the beginning of the span.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryWriteBFloat16BigEndian(Span<byte> destination, BFloat16 value)
{
if (BitConverter.IsLittleEndian)
{
short tmp = ReverseEndianness(BitConverter.BFloat16ToInt16Bits(value));
return MemoryMarshal.TryWrite(destination, in tmp);
}

return MemoryMarshal.TryWrite(destination, in value);
}

/// <summary>
/// Writes a <see cref="short" /> into a span of bytes, as big endian.
/// </summary>
Expand Down
Loading
Loading