Skip to content

Commit

Permalink
Implemented Fixed16
Browse files Browse the repository at this point in the history
  • Loading branch information
MineCake147E committed Dec 8, 2019
1 parent d0c7138 commit 49693e4
Show file tree
Hide file tree
Showing 2 changed files with 328 additions and 0 deletions.
295 changes: 295 additions & 0 deletions MonoAudio/Primitives/Fixed16.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace MonoAudio.Primitives
{
/// <summary>
/// Represents a Fixed-Point Number in Signed Q0.15 format using <a href="https://en.wikipedia.org/wiki/Two%27s_complement">Two's Complement</a> format.<br/>
/// For reference of "Qm.n" notation: See <a href="https://source.android.com/devices/audio/data_formats#fixed">here</a> and <a href="https://en.wikipedia.org/wiki/Q_(number_format)">here</a>.
/// </summary>
[StructLayout(LayoutKind.Explicit, Size = 2)]
public readonly struct Fixed16 : IEquatable<Fixed16>, IComparable<Fixed16>
{
/// <summary>
/// The internal value stored in memory.
/// </summary>
[FieldOffset(0)]
public readonly short Value;

#region Constants

private const float V2FRatio = 0.000030517578125f;

/// <summary>
/// Represents the largest possible value of <see cref="OffsetSByte"/>. This field is constant and read-only.
/// </summary>
public static readonly Fixed16 MaxValue = new Fixed16(short.MaxValue);

/// <summary>
/// Represents the smallest possible value of <see cref="OffsetSByte"/>. This field is constant and read-only.
/// </summary>
public static readonly Fixed16 MinValue = new Fixed16(short.MaxValue);

/// <summary>
/// Represents the number zero (0).
/// </summary>
public static readonly Fixed16 Zero = new Fixed16(0);

/// <summary>
/// Represents the smallest positive <see cref="Fixed16"/> value that is greater than zero. This field is constant and read-only.
/// </summary>
public static readonly Fixed16 Epsilon = new Fixed16(1);

#endregion Constants

/// <summary>
/// Initializes a new instance of the <see cref="Fixed16"/> struct.
/// </summary>
/// <param name="internalValue">The internal value stored in memory.</param>
public Fixed16(short internalValue)
{
Value = internalValue;
}

/// <summary>
/// Gets the value represented in <see cref="float"/>.
/// </summary>
/// <value>
/// The float value.
/// </value>
public float FloatValue => Value * V2FRatio;

#region Arithmetics

/// <summary>
/// Adds two specified <see cref="Fixed16"/> values.
/// </summary>
/// <param name="left">The first value to add.</param>
/// <param name="right">The second value to add.</param>
/// <returns>
/// The result of adding <paramref name="left"/> and <paramref name="right"/>.
/// </returns>
public static Fixed16 operator +(Fixed16 left, Fixed16 right) => new Fixed16((short)(left.Value + right.Value));

/// <summary>
/// Subtracts two specified <see cref="Fixed16"/> values.
/// </summary>
/// <param name="left">The minuend.</param>
/// <param name="right">The subtrahend.</param>
/// <returns>
/// The result of subtracting <paramref name="right"/> from <paramref name="left"/>.
/// </returns>
public static Fixed16 operator -(Fixed16 left, Fixed16 right) => new Fixed16((short)(left.Value - right.Value));

/// <summary>
/// Multiplies two specified <see cref="Fixed16"/> values.
/// </summary>
/// <param name="left">The first value to multiply.</param>
/// <param name="right">The second value to multiply.</param>
/// <returns>
/// The result of multiplying <paramref name="left"/> by <paramref name="right"/>.
/// </returns>
public static Fixed16 operator *(Fixed16 left, Fixed16 right) => new Fixed16((short)((left.Value * right.Value) >> 15));

/// <summary>
/// Divides two specified <see cref="Fixed16"/> values.
/// </summary>
/// <param name="left">The dividend.</param>
/// <param name="right">The divisor.</param>
/// <returns>
/// The result of dividing <paramref name="left"/> by <paramref name="right"/>.
/// </returns>
public static double operator /(Fixed16 left, Fixed16 right) => (double)left.Value / right.Value;

/// <summary>
/// Returns the remainder resulting from dividing two specified <see cref="Fixed16"/> values.
/// </summary>
/// <param name="left">The dividend.</param>
/// <param name="right">The divisor.</param>
/// <returns>
/// The remainder resulting from dividing <paramref name="left"/> by <paramref name="right"/>.
/// </returns>
public static Fixed16 operator %(Fixed16 left, Fixed16 right) => new Fixed16((short)(left.Value % right.Value));

/// <summary>
/// Returns the value of the <see cref="Fixed16"/> operand (the sign of the operand is unchanged).
/// </summary>
/// <param name="value">The operand to return.</param>
/// <returns>
/// The value of the operand, <paramref name="value"/>.
/// </returns>
public static Fixed16 operator +(Fixed16 value) => value;

/// <summary>
/// Increments the <see cref="Fixed16"/> operand by <see cref="Epsilon"/>.
/// </summary>
/// <param name="value">The value to increment.</param>
/// <returns>
/// The value of <paramref name="value"/> incremented by <see cref="Epsilon"/>.
/// </returns>
public static Fixed16 operator ++(Fixed16 value) => new Fixed16((short)(value.Value + 1));

/// <summary>
/// Negates the value of the specified <see cref="Fixed16"/> operand.
/// </summary>
/// <param name="value">The value to negate.</param>
/// <returns>
/// The result of <paramref name="value"/> multiplied by negative one (-1).
/// </returns>
public static Fixed16 operator -(Fixed16 value) => new Fixed16((short)-value.Value);

/// <summary>
/// Decrements the <see cref="Fixed16"/> operand by <see cref="Epsilon"/>.
/// </summary>
/// <param name="value">The value to decrement.</param>
/// <returns>
/// The value of <paramref name="value"/> decremented by <see cref="Epsilon"/>.
/// </returns>
public static Fixed16 operator --(Fixed16 value) => new Fixed16((short)(value.Value - 1));

#endregion Arithmetics

#region Conversion

/// <summary>
/// Performs an implicit conversion from <see cref="Fixed16"/> to <see cref="float"/>.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>
/// The result of the conversion.
/// </returns>
public static implicit operator float(Fixed16 value) => value.FloatValue;

/// <summary>
/// Performs an explicit conversion from <see cref="Fixed16"/> to <see cref="float"/>.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>
/// The result of the conversion.
/// </returns>
public static explicit operator double(Fixed16 value) => value.FloatValue;

/// <summary>
/// Performs an explicit conversion from <see cref="float"/> to <see cref="Fixed16"/>.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>
/// The result of the conversion.
/// </returns>
public static explicit operator Fixed16(float value) => new Fixed16((short)(value * 32768.0f));

/// <summary>
/// Performs an explicit conversion from <see cref="double"/> to <see cref="Fixed16"/>.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>
/// The result of the conversion.
/// </returns>
public static explicit operator Fixed16(double value) => new Fixed16((short)(value * 32768.0));

#endregion Conversion

#region Comparison

/// <summary>
/// Compares this instance to a specified 16-bit signed fixed-point number and returns an indication of their relative values.
/// </summary>
/// <param name="other">An number to compare.</param>
/// <returns>A signed number indicating the relative values of this instance and <paramref name="other"/>.</returns>
public int CompareTo(Fixed16 other) => other.Value.CompareTo(Value);

/// <summary>
/// Returns a value indicating whether a specified <see cref="Fixed16"/> is less than another specified <see cref="Fixed16"/>.
/// </summary>
/// <param name="left">The first value to compare.</param>
/// <param name="right">The second value to compare.</param>
/// <returns>
/// <c>true</c> if <paramref name="left"/> is less than <paramref name="right"/>; otherwise, <c>false</c>.
/// </returns>
public static bool operator <(Fixed16 left, Fixed16 right) => left.Value < right.Value;

/// <summary>
/// Returns a value indicating whether a specified <see cref="Fixed16"/> is less than or equal to another specified <see cref="Fixed16"/>.
/// </summary>
/// <param name="left">The first value to compare.</param>
/// <param name="right">The second value to compare.</param>
/// <returns>
/// <c>true</c> if <paramref name="left"/> is less than or equal to <paramref name="right"/>; otherwise, <c>false</c>.
/// </returns>
public static bool operator <=(Fixed16 left, Fixed16 right) => left.Value <= right.Value;

/// <summary>
/// Returns a value indicating whether a specified <see cref="Fixed16"/> is greater than another specified <see cref="Fixed16"/>.
/// </summary>
/// <param name="left">The first value to compare.</param>
/// <param name="right">The second value to compare.</param>
/// <returns>
/// <c>true</c> if <paramref name="left"/> is greater than <paramref name="right"/>; otherwise, <c>false</c>.
/// </returns>
public static bool operator >(Fixed16 left, Fixed16 right) => left.Value > right.Value;

/// <summary>
/// Returns a value indicating whether a specified <see cref="Fixed16"/> is greater than or equal to another specified <see cref="Fixed16"/>.
/// </summary>
/// <param name="left">The first value to compare.</param>
/// <param name="right">The second value to compare.</param>
/// <returns>
/// <c>true</c> if <paramref name="left"/> is greater than or equal to <paramref name="right"/>; otherwise, <c>false</c>.
/// </returns>
public static bool operator >=(Fixed16 left, Fixed16 right) => left.Value >= right.Value;

#endregion Comparison

#region Equality

/// <summary>
/// Indicates whether the current object is equal to another object of the same type.
/// </summary>
/// <param name="obj">An object to compare with this object.</param>
/// <returns>
/// <c>true</c> if the current object is equal to the obj parameter; otherwise, <c>false</c>.
/// </returns>
public override bool Equals(object obj) => obj is Fixed16 @fixed && Equals(@fixed);

/// <summary>
/// Indicates whether the current object is equal to another object of the same type.
/// </summary>
/// <param name="other">An object to compare with this object.</param>
/// <returns>
/// <c>true</c> if the current object is equal to the other parameter; otherwise, <c>false</c>.
/// </returns>
public bool Equals(Fixed16 other) => Value == other.Value;

/// <summary>
/// Returns a hash code for this instance.
/// </summary>
/// <returns>
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
/// </returns>
public override int GetHashCode() => -1937169414 + Value.GetHashCode();

/// <summary>
/// Indicates whether the values of two specified <see cref="Fixed16"/> objects are equal.
/// </summary>
/// <param name="left">The first <see cref="Fixed16"/> to compare.</param>
/// <param name="right">The second <see cref="Fixed16"/> to compare.</param>
/// <returns>
/// <c>true</c> if the left is the same as the right; otherwise, <c>false</c>.
/// </returns>
public static bool operator ==(Fixed16 left, Fixed16 right) => left.Equals(right);

/// <summary>
/// Indicates whether the values of two specified <see cref="Fixed16"/> objects are not equal.
/// </summary>
/// <param name="left">The first <see cref="Fixed16"/> to compare.</param>
/// <param name="right">The second <see cref="Fixed16"/> to compare.</param>
/// <returns>
/// <c>true</c> if left and right are not equal; otherwise, <c>false</c>.
/// </returns>
public static bool operator !=(Fixed16 left, Fixed16 right) => !(left == right);

#endregion Equality
}
}
33 changes: 33 additions & 0 deletions MonoAudio/Primitives/OffsetSByte.cs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,39 @@ public static explicit operator sbyte(OffsetSByte value)
/// </returns>
public static explicit operator OffsetSByte(int value) => (OffsetSByte)unchecked((sbyte)value);

/// <summary>
/// Converts the numeric value of this instance to its equivalent string representation.
/// </summary>
/// <returns>
/// A <see cref="string" /> that represents the value of this instance.
/// </returns>
public override string ToString() => ((sbyte)this).ToString();

/// <summary>
/// Converts the string representation of a number to its <see cref="OffsetSByte"/> equivalent.
/// </summary>
/// <param name="s">The string representation of the number to convert.</param>
/// <returns>The equivalent to the number contained in <paramref name="s"/>.</returns>
public static OffsetSByte Parse(string s) => new OffsetSByte(sbyte.Parse(s));

/// <summary>
/// Converts the string representation of a number to its <see cref="OffsetSByte"/> equivalent.<br/>
/// A return value indicates whether the conversion succeeded or failed.
/// </summary>
/// <param name="s">The string representation of the number to convert.</param>
/// <param name="result">When this method returns, contains the <see cref="OffsetSByte"/> number that is equivalent to the numeric value contained in <paramref name="s"/>, if the conversion succeeded, or zero if the conversion failed.
/// The conversion fails if the <paramref name="s"/> parameter is <c>null</c> or <see cref="string.Empty"/>, is not a number in a valid format, or represents a number less than <see cref="MinValue"/> or greater than <see cref="MaxValue"/>.
/// This parameter is passed uninitialized; any value originally supplied in result is overwritten.</param>
/// <returns>
/// <c>true</c> if <paramref name="s"/> was converted successfully; otherwise, <c>false</c>.
/// </returns>
public static bool TryParse(string s, out OffsetSByte result)
{
var g = sbyte.TryParse(s, out var b);
result = new OffsetSByte(b);
return g;
}

#endregion Conversion

#region Comparison
Expand Down

0 comments on commit 49693e4

Please sign in to comment.