#include "numeric_conversion.h" #include #include "hardware_seam.h" #include "program_memory.h" namespace meCoffee { namespace { // binary32 bit patterns for 10^-w and 10^+w with w = 1, 2, 4, 8, 16, 32. const uint32_t kNegativeDecimalPowerBitPatterns[6] MECOFFEE_PROGRAM_MEMORY = { 0x3dcccccdUL, 0x3c23d70aUL, 0x38d1b717UL, 0x322bcc77UL, 0x24e69595UL, 0x0a4fb11fUL}; const uint32_t kPositiveDecimalPowerBitPatterns[6] MECOFFEE_PROGRAM_MEMORY = { 0x41200000UL, 0x42c80000UL, 0x461c4000UL, 0x4cbebc20UL, 0x5a0e1bcaUL, 0x749dc5aeUL}; const uint8_t kDecimalPowerWeights[6] = {1, 2, 4, 8, 16, 32}; constexpr uint32_t kSignificandOverflowThreshold = 429496728UL; constexpr uint16_t kExponentDigitFreezeThreshold = 3200; constexpr uint32_t kCanonicalNotANumberBitPattern = 0x7fc00000UL; constexpr uint32_t kPositiveInfinityBitPattern = 0x7f800000UL; float reinterpretBitPatternAsFloat(uint32_t bitPattern) { float reinterpretedValue; memcpy(&reinterpretedValue, &bitPattern, sizeof reinterpretedValue); return reinterpretedValue; } uint32_t reinterpretFloatAsBitPattern(float numericValue) { uint32_t bitPattern; memcpy(&bitPattern, &numericValue, sizeof bitPattern); return bitPattern; } uint32_t readProgramMemoryUnsigned32(const uint32_t* programMemoryAddress) { const uint8_t* firstByte = reinterpret_cast(programMemoryAddress); uint32_t assembledValue = 0; for (uint8_t byteIndex = 0; byteIndex < 4; ++byteIndex) { assembledValue |= static_cast(hardware::readProgramMemoryByte(firstByte + byteIndex)) << (8 * byteIndex); } return assembledValue; } bool isWhitespaceCharacter(char character) { return character == ' ' || (character >= 0x09 && character <= 0x0d); } bool isDecimalDigit(char character) { return character >= '0' && character <= '9'; } char toLowerCaseLetter(char character) { return (character >= 'A' && character <= 'Z') ? static_cast(character + ('a' - 'A')) : character; } bool startsWithCaseInsensitive(const char* text, const char* lowerCasePrefix) { while (*lowerCasePrefix != '\0') { if (toLowerCaseLetter(*text) != *lowerCasePrefix) { return false; } ++text; ++lowerCasePrefix; } return true; } } // namespace float parseDecimalFloatingPoint(const char* text, DecimalParseDiagnostics* diagnostics) { DecimalParseDiagnostics localDiagnostics = {}; DecimalParseDiagnostics& recorded = diagnostics != nullptr ? *diagnostics : localDiagnostics; recorded = DecimalParseDiagnostics{}; const char* cursor = text; while (isWhitespaceCharacter(*cursor)) { ++cursor; } bool isNegative = false; if (*cursor == '-') { isNegative = true; ++cursor; } else if (*cursor == '+') { ++cursor; } if (startsWithCaseInsensitive(cursor, "nan")) { recorded.consumedCharacterCount = static_cast(cursor + 3 - text); return reinterpretBitPatternAsFloat(kCanonicalNotANumberBitPattern); } if (startsWithCaseInsensitive(cursor, "inf")) { cursor += 3; if (startsWithCaseInsensitive(cursor, "inity")) { cursor += 5; } recorded.consumedCharacterCount = static_cast(cursor - text); return reinterpretBitPatternAsFloat(kPositiveInfinityBitPattern | (isNegative ? 0x80000000UL : 0)); } uint32_t significand = 0; uint16_t decimalExponentOffset = 0; uint16_t discardedDigitCount = 0; bool hasSignificandDigit = false; bool hasSeenDecimalPoint = false; bool isDiscardingDigits = false; for (;; ++cursor) { const char character = *cursor; if (isDecimalDigit(character)) { hasSignificandDigit = true; if (isDiscardingDigits) { ++discardedDigitCount; if (!hasSeenDecimalPoint) { ++decimalExponentOffset; } } else { if (hasSeenDecimalPoint) { --decimalExponentOffset; } significand = significand * 10 + static_cast(character - '0'); if (significand >= kSignificandOverflowThreshold) { isDiscardingDigits = true; } } } else if (character == '.' && !hasSeenDecimalPoint) { hasSeenDecimalPoint = true; } else { break; } } if (!hasSignificandDigit) { return 0.0f; } if (*cursor == 'e' || *cursor == 'E') { const char* exponentCursor = cursor + 1; bool isExponentNegative = false; if (*exponentCursor == '-') { isExponentNegative = true; ++exponentCursor; } else if (*exponentCursor == '+') { ++exponentCursor; } if (isDecimalDigit(*exponentCursor)) { uint16_t explicitExponent = 0; while (isDecimalDigit(*exponentCursor)) { if (explicitExponent < kExponentDigitFreezeThreshold) { explicitExponent = static_cast(explicitExponent * 10 + (*exponentCursor - '0')); } ++exponentCursor; } decimalExponentOffset = static_cast( decimalExponentOffset + (isExponentNegative ? static_cast(0 - explicitExponent) : explicitExponent)); cursor = exponentCursor; } } recorded.retainedSignificand = significand; recorded.decimalExponentOffset = static_cast(decimalExponentOffset); recorded.discardedSignificandDigitCount = discardedDigitCount; recorded.consumedCharacterCount = static_cast(cursor - text); float scaledValue = static_cast(significand); if (isNegative) { scaledValue = -scaledValue; } if (significand == 0) { return scaledValue; } const int16_t signedExponentOffset = static_cast(decimalExponentOffset); const bool isScalingDownward = signedExponentOffset < 0; uint16_t remainingExponentMagnitude = isScalingDownward ? static_cast(0 - static_cast(signedExponentOffset)) : static_cast(signedExponentOffset); const uint32_t* powerTable = isScalingDownward ? kNegativeDecimalPowerBitPatterns : kPositiveDecimalPowerBitPatterns; for (int8_t weightIndex = 5; weightIndex >= 0; --weightIndex) { const float decimalPower = reinterpretBitPatternAsFloat(readProgramMemoryUnsigned32(&powerTable[weightIndex])); while (remainingExponentMagnitude >= kDecimalPowerWeights[weightIndex]) { scaledValue *= decimalPower; remainingExponentMagnitude = static_cast(remainingExponentMagnitude - kDecimalPowerWeights[weightIndex]); } } const uint32_t scaledBitPattern = reinterpretFloatAsBitPattern(scaledValue); const bool isInfinite = (scaledBitPattern & 0x7fffffffUL) == kPositiveInfinityBitPattern; const bool isZero = (scaledBitPattern & 0x7fffffffUL) == 0; recorded.isOutOfRangeIndicatorRaised = isInfinite || isZero; return scaledValue; } uint32_t convertFloatToUnsigned32(float numericValue, bool* isConversionStatusFlagged) { bool isFlagged = false; uint32_t convertedValue = 0; const uint32_t bitPattern = reinterpretFloatAsBitPattern(numericValue); const bool isNegative = (bitPattern & 0x80000000UL) != 0; const uint8_t biasedExponent = static_cast((bitPattern >> 23) & 0xff); if (biasedExponent == 0xff) { isFlagged = true; } else if (biasedExponent >= 127) { const uint8_t unbiasedExponent = static_cast(biasedExponent - 127); if (unbiasedExponent >= 32) { isFlagged = true; } else { const uint32_t significandWithHiddenBit = (bitPattern & 0x007fffffUL) | 0x00800000UL; const uint32_t magnitude = unbiasedExponent >= 23 ? significandWithHiddenBit << (unbiasedExponent - 23) : significandWithHiddenBit >> (23 - unbiasedExponent); if (magnitude >= 0x80000000UL) { isFlagged = true; } convertedValue = isNegative ? static_cast(0 - magnitude) : magnitude; } } if (isConversionStatusFlagged != nullptr) { *isConversionStatusFlagged = isFlagged; } return convertedValue; } int32_t convertFloatToSigned32(float numericValue) { bool isFlagged = false; const uint32_t convertedValue = convertFloatToUnsigned32(numericValue, &isFlagged); if (isFlagged) { return INT32_MIN; } return static_cast(convertedValue); } namespace { uint32_t parseIntegerPrefix(const char* text, uint8_t bitWidth) { const char* cursor = text; while (isWhitespaceCharacter(*cursor)) { ++cursor; } bool isNegative = false; if (*cursor == '-') { isNegative = true; ++cursor; } else if (*cursor == '+') { ++cursor; } uint32_t accumulated = 0; while (isDecimalDigit(*cursor)) { accumulated = accumulated * 10 + static_cast(*cursor - '0'); ++cursor; } if (isNegative) { accumulated = 0 - accumulated; } return bitWidth == 16 ? (accumulated & 0xffffUL) : accumulated; } } // namespace uint16_t parseIntegerPrefixModulo65536(const char* text) { return static_cast(parseIntegerPrefix(text, 16)); } uint32_t parseIntegerPrefixModulo4294967296(const char* text) { return parseIntegerPrefix(text, 32); } uint8_t formatUnsignedDecimal(uint32_t numericValue, char* outputBuffer) { char reversedDigits[10]; uint8_t digitCount = 0; do { reversedDigits[digitCount++] = static_cast('0' + numericValue % 10); numericValue /= 10; } while (numericValue != 0); for (uint8_t position = 0; position < digitCount; ++position) { outputBuffer[position] = reversedDigits[digitCount - 1 - position]; } return digitCount; } uint8_t formatSignedDecimal(int32_t numericValue, char* outputBuffer) { if (numericValue < 0) { outputBuffer[0] = '-'; return static_cast(1 + formatUnsignedDecimal(0 - static_cast(numericValue), outputBuffer + 1)); } return formatUnsignedDecimal(static_cast(numericValue), outputBuffer); } } // namespace meCoffee