2016-08-12 4 views
1

Я боюсь с преобразованием с плавающей точкой в ​​шестнадцатеричное преобразование в Lua. Мое приложение связывается со старым сэмплером Akai S2000. Сэмплер кодирует два байтовых сообщения на четыре значения. Грызуны находятся в обратном порядке, следовательно, самый значительный полубайт является последним. Существует один параметр, который использует двоичную кодировку частичной части значения. Два кубика MS используются для кодирования неотъемлемой части значения, а LS nibbles используются для кодирования двоичной фракции.Преобразование значений с плавающей запятой в шестнадцатеричный

Основываясь на этом обсуждении https://bytes.com/topic/c/answers/219928-how-convert-float-hex Я начал реализовывать алгоритм Lua для генерации этих значений nibble из заданного значения параметра. Поскольку я не настолько силен с небольшими вычислениями, я думаю, что я делаю много вещей не так. Должен быть более простой способ вычислить эти значения и избежать многих моих глупых взломов if/else.

Мой код (вставленный ниже) работает для положительных чисел, но с отрицательными дробями намного сложнее.

В моих тестах я добавил к таблицам с ожидаемыми значениями. Каждый стол работает следующим образом:

key = give value * 100 
value = expected outcome from my algorithm 

I.e. первая запись в таблице positiveNumbers представляет входное значение 0,01, а ожидаемым выходом для этого значения является четырехбайтовый MemoryBlock, содержащий 02 00 00 00 (первые два байта представляют собой долю, а последние две - неотъемлемую часть).

В настоящее время мой алгоритм терпит неудачу при -0.94, и я не могу взломать его, не нарушая какое-либо другое значение.

Есть ли кто-нибудь, кто является строкой с битовыми вычислениями, и что легко увидеть любую ошибку noob, которую я сделал специально с преобразованием отрицательных значений? Любая помощь или указатели будут оценены!

Lua Код:

function float2nibbles(value) 
    local nibbles = MemoryBlock(4, true) 

    -- Retreive integral and fraction parts of the given value to be converted 
    local integ, fract = math.modf(math.abs(value)) 

    -- Calculate the values of the integral part (last two nibbles) 
    local bi = BigInteger(integ) 
    if value < 0 then 
     -- This variable is sometimes added in the negative conversion of the MS nibbles 
     local lsAdd = 1 
     if integ == 0 then 
      lsAdd = 0 
     end 
     nibbles:setByte(2, bit.band(bit.bnot(bi:getBitRangeAsInt(0,4)) + lsAdd, 0xF)) 
     nibbles:setByte(3, bit.band(bit.bnot(bi:getBitRangeAsInt(4,4)), 0xF)) 
    else 
     nibbles:setByte(2, bit.band(bi:getBitRangeAsInt(0,4), 0xF)) 
     nibbles:setByte(3, bit.band(bi:getBitRangeAsInt(4,4), 0xF)) 
    end 

    -- Calculate the values of the fraction (first two nibbles) 
    local remainder = fract 
    local prevRemain = 0 
    for i = 1,2 do 
     remainder = remainder * 16 
     -- Integral part of the remainder 
     local d = math.modf(remainder) 
     if value < 0 and fract ~= 0 then 
      local lsAdd = 1 
      if fract == 0 or i == 1 then 
       lsAdd = 0 
      end 
      console(string.format("lsAdd %d", lsAdd)) 
      nibbles:setByte(2 - i, bit.band(bit.bnot(d) + lsAdd, 0xF)) 
     else 
      nibbles:setByte(2 - i, bit.band(d, 0xF)) 
     end 
     console(string.format("fract %d = %d, %.2f", i, d, remainder)) 
     prevRemain = remainder 
     remainder = remainder - d 
    end 

    -- For some reason this increment helps when the LS nibble should increment the value of the second nibble 
    if nibbles:getByte(0) == 0 and nibbles:getByte(1) ~= 0 and value < 0 then 
     console(string.format("weird increment { %d %d }", nibbles:getByte(0), nibbles:getByte(1))) 
     nibbles:setByte(1, nibbles:getByte(1) + 1) 
    end 

    -- The precision of this data is one byte but apparently they seem to use a third increment to check for rounding 
    remainder = remainder * 16 
    console(string.format("final remainder %.2f", remainder)) 
    if math.abs(remainder - prevRemain) > 0.001 and remainder > 14 then 
     console(string.format("overflow -> %.2f (%.2f)", remainder, prevRemain)) 
     if value < 0 then 
      nibbles:setByte(0, nibbles:getByte(0) - 1) 
     else 
      nibbles:setByte(0, nibbles:getByte(0) + 1) 
     end 
    end 

    console(string.format("%.2f : integral part %s (%s), fract %.2f", value, bit.tohex(integ, 2), nibbles:toHexString(1), fract)) 
    return nibbles 
end 

local positiveNumbers = { 
    "02 00 00 00", 
    "05 00 00 00", 
    "07 00 00 00", 
    "0A 00 00 00", 
    "0C 00 00 00", 
    "0F 00 00 00", 
    "02 01 00 00", 
    "04 01 00 00", 
    "07 01 00 00", 
    "09 01 00 00", 
    "0C 01 00 00", 
    "0E 01 00 00", 
    "01 02 00 00", 
    "03 02 00 00", 
    "06 02 00 00", 
    "09 02 00 00", 
    "0B 02 00 00", 
    "0E 02 00 00", 
    "00 03 00 00", 
    "03 03 00 00", 
    "05 03 00 00", 
    "08 03 00 00", 
    "0B 03 00 00", 
    "0D 03 00 00", 
    "00 04 00 00", 
    "02 04 00 00", 
    "05 04 00 00", 
    "07 04 00 00", 
    "0A 04 00 00", 
    "0C 04 00 00", 
    "0F 04 00 00", 
    "02 05 00 00", 
    "04 05 00 00", 
    "07 05 00 00", 
    "09 05 00 00", 
    "0C 05 00 00", 
    "0E 05 00 00", 
    "01 06 00 00", 
    "03 06 00 00", 
    "06 06 00 00", 
    "09 06 00 00", 
    "0B 06 00 00", 
    "0E 06 00 00", 
    "00 07 00 00", 
    "03 07 00 00", 
    "05 07 00 00", 
    "08 07 00 00", 
    "0B 07 00 00", 
    "0D 07 00 00", 
    "00 08 00 00", 
    "02 08 00 00", 
    "05 08 00 00", 
    "07 08 00 00", 
    "0A 08 00 00", 
    "0C 08 00 00", 
    "0F 08 00 00", 
    "02 09 00 00", 
    "04 09 00 00", 
    "07 09 00 00", 
    "09 09 00 00", 
    "0C 09 00 00", 
    "0E 09 00 00", 
    "01 0A 00 00", 
    "03 0A 00 00", 
    "06 0A 00 00", 
    "09 0A 00 00", 
    "0B 0A 00 00", 
    "0E 0A 00 00", 
    "00 0B 00 00", 
    "03 0B 00 00", 
    "05 0B 00 00", 
    "08 0B 00 00", 
    "0B 0B 00 00", 
    "0D 0B 00 00", 
    "00 0C 00 00", 
    "02 0C 00 00", 
    "05 0C 00 00", 
    "07 0C 00 00", 
    "0A 0C 00 00", 
    "0C 0C 00 00", 
    "0F 0C 00 00", 
    "02 0D 00 00", 
    "04 0D 00 00", 
    "07 0D 00 00", 
    "09 0D 00 00", 
    "0C 0D 00 00", 
    "0E 0D 00 00", 
    "01 0E 00 00", 
    "03 0E 00 00", 
    "06 0E 00 00", 
    "09 0E 00 00", 
    "0B 0E 00 00", 
    "0E 0E 00 00", 
    "00 0F 00 00", 
    "03 0F 00 00", 
    "05 0F 00 00", 
    "08 0F 00 00", 
    "0B 0F 00 00", 
    "0D 0F 00 00", 
    "00 00 01 00" 
} 

local negativeNumbers = { 
    "0E 0F 0F 0F", 
    "0B 0F 0F 0F", 
    "09 0F 0F 0F", 
    "06 0F 0F 0F", 
    "04 0F 0F 0F", 
    "01 0F 0F 0F", 
    "0E 0E 0F 0F", 
    "0C 0E 0F 0F", 
    "09 0E 0F 0F", 
    "07 0E 0F 0F", 
    "04 0E 0F 0F", 
    "02 0E 0F 0F", 
    "0F 0D 0F 0F", 
    "0D 0D 0F 0F", 
    "0A 0D 0F 0F", 
    "07 0D 0F 0F", 
    "05 0D 0F 0F", 
    "02 0D 0F 0F", 
    "00 0D 0F 0F", 
    "0D 0C 0F 0F", 
    "0B 0C 0F 0F", 
    "08 0C 0F 0F", 
    "05 0C 0F 0F", 
    "03 0C 0F 0F", 
    "00 0C 0F 0F", 
    "0E 0B 0F 0F", 
    "0B 0B 0F 0F", 
    "09 0B 0F 0F", 
    "06 0B 0F 0F", 
    "04 0B 0F 0F", 
    "01 0B 0F 0F", 
    "0E 0A 0F 0F", 
    "0C 0A 0F 0F", 
    "09 0A 0F 0F", 
    "07 0A 0F 0F", 
    "04 0A 0F 0F", 
    "02 0A 0F 0F", 
    "0F 09 0F 0F", 
    "0D 09 0F 0F", 
    "0A 09 0F 0F", 
    "07 09 0F 0F", 
    "05 09 0F 0F", 
    "02 09 0F 0F", 
    "00 09 0F 0F", 
    "0D 08 0F 0F", 
    "0B 08 0F 0F", 
    "08 08 0F 0F", 
    "05 08 0F 0F", 
    "03 08 0F 0F", 
    "00 08 0F 0F", 
    "0E 07 0F 0F", 
    "0B 07 0F 0F", 
    "09 07 0F 0F", 
    "06 07 0F 0F", 
    "04 07 0F 0F", 
    "01 07 0F 0F", 
    "0E 06 0F 0F", 
    "0C 06 0F 0F", 
    "09 06 0F 0F", 
    "07 06 0F 0F", 
    "04 06 0F 0F", 
    "02 06 0F 0F", 
    "0F 05 0F 0F", 
    "0D 05 0F 0F", 
    "0A 05 0F 0F", 
    "07 05 0F 0F", 
    "05 05 0F 0F", 
    "02 05 0F 0F", 
    "00 05 0F 0F", 
    "0D 04 0F 0F", 
    "0B 04 0F 0F", 
    "08 04 0F 0F", 
    "05 04 0F 0F", 
    "03 04 0F 0F", 
    "00 04 0F 0F", 
    "0E 03 0F 0F", 
    "0B 03 0F 0F", 
    "09 03 0F 0F", 
    "06 03 0F 0F", 
    "04 03 0F 0F", 
    "01 03 0F 0F", 
    "0E 02 0F 0F", 
    "0C 02 0F 0F", 
    "09 02 0F 0F", 
    "07 02 0F 0F", 
    "04 02 0F 0F", 
    "02 02 0F 0F", 
    "0F 01 0F 0F", 
    "0D 01 0F 0F", 
    "0A 01 0F 0F", 
    "07 01 0F 0F", 
    "05 01 0F 0F", 
    "02 01 0F 0F", 
    "00 01 0F 0F", 
    "0D 00 0F 0F", 
    "0B 00 0F 0F", 
    "08 00 0F 0F", 
    "05 00 0F 0F", 
    "03 00 0F 0F", 
    "00 00 0F 0F" 
} 

function verifyFloat2Nibbles(value, expectedMemBlock) 
    local temp = string.upper(float2nibbles(value):toHexString(1)) 
    assert(expectedMemBlock == temp, 
     string.format("Incorrect result for %.2f, expected %s, got %s", value, expectedMemBlock, temp)) 
end 

for k,v in pairs(positiveNumbers) do 
    verifyFloat2Nibbles(k/100, v) 
end 

for k,v in pairs(negativeNumbers) do 
    verifyFloat2Nibbles((k/100) * -1, v) 
end 

ответ

0
function float2nibbles(value) 
    local nibbles = MemoryBlock(4, true) 
    local n = math.floor(math.abs(value)*256 + 0.13) 
    n = value < 0 and 0x10000 - n or n 
    for pos = 0, 3 do 
     nibbles:setByte(pos, n%16) 
     n = math.floor(n/16) 
    end 
    return nibbles 
end 
+0

Спасибо! Ваш код отлично работает. Но в чем смысл значения 0.13? Где вы контролируете, сколько шестнадцатеричных цифр будет представлять собой неотъемлемую часть значения? – pascalc

+0

'0.13' можно заменить любым числом из диапазона' 0.12

0

Одна из проблем, у вас есть то, что, когда вы отрицать, что вы не распространяющиеся перенос от одного клева к другим. Код слишком сложный для просмотра и исправления. Вот повтор:

n2a = {[0]='0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'} 

function f2h (f) 
    -- TODO should range check f 
    local scaledf = f * 256 -- move binary point 8 bits right 
    -- TODO optionally you could also round scaledf here 
    local scaledi = math.modf(scaledf) -- integer part 
    -- convert to nibbles 
    local scalediparts = { (0xf & scaledi), 
          (0xf & (scaledi >> 4)), 
          (0xf & (scaledi >> 8)), 
          (0xf & (scaledi >> 12)) } 
    -- output in hex 
    print(n2a[scalediparts[1]], n2a[scalediparts[2]], n2a[scalediparts[3]], n2a[scalediparts[4]]) 
    -- return the nibbles 
    return scalediparts 
end 

и тест консоли:

> f2h(-0.01) 
e f f f 
table: 0x7feaf9d06390 
> f2h(-0.94) 
0 1 f f 
table: 0x7feaf9e009a0 
> f2h(0.01) 
2 0 0 0 
table: 0x7feaf9d06410 
> 

Это использует Lua 5.3 битовые операторы; конвертация в библиотеку bit проста. Вам также потребуется изменить оператор печати и вернуть значение в соответствии с вашими предпочтениями.

+0

Хорошее решение, я обновил ваш код, чтобы использовать библиотеку бит. К сожалению, алгоритм терпит неудачу: «Неверный результат для 0.07, ожидаемый 02 01 00 00, получил 01 01 00 00» – pascalc

+0

Да, вы увидите, что решение Егора имеет магический '+ 0.13', чтобы заставить его работать с вашей таблицей. Это округление, о котором я упоминал в комментарии. Обратите внимание, что 0,13/256 составляет 0,005. –

Смежные вопросы