При подключении к «1C: Предприятие 8.3» в узле импорта и/или в мастере подключений возникает ошибка «Invalid floating point operation». Что делать?
Данная ошибка может возникать при выполнении узлов импорта из 1С или при тестировании настроенного подключения. Диагностируется при интеграции с платформой 1С версии 8.3.10, использующей серверную архитектуру.
В случае если сценарий импорта из 1С выполняется интерактивно в интерфейсе DStudio, решение для данной ошибки следующее:
- Открыть в текстовом редакторе файл сценария (расширение «ded»);
- Найти раздел «EnvironmentVariables» и в описании переменной «DisableFloatExceptions» добавить строку
<Value>1</Value>
:... <EnvironmentVariables> <Version>1</Version> <Vars> <Count>2</Count> <I_0> <Name>LocaleID</Name> <DataType>dtInteger</DataType> <Value>1049</Value> </I_0> <I_1> <Name>DisableFloatExceptions</Name> <DataType>dtBoolean</DataType> <Value>1</Value> \Эта строка добавляется! </I_1> </Vars> </EnvironmentVariables> ...
- Сохранить файл сценария.
Значение переменной «DisableFloatExceptions» при правильной правке сценария отразится в настройках Deductor (Рис. 2)
Важно: При изменении и последующем сохранении сценария в DStudio данные изменения могут быть утеряны.
В случае если сценарий используется в пакетном режиме, решение следующее:
Необходимо использовать управляющий сценарий, в котором при помощи узла «Команда ОС» запускать на выполнение DStudio.exe с параметрами запуска сценария импорта из 1С. При этом сам сценарий импорта из 1С править вышеописанным способом не надо, значение переменной DisableFloatExceptions можно передавать в параметрах запуска сценария. В узле «Команда ОС» строка запуска будет примерно такой:
chcp 1251
"C:Program Files (x86)BaseGroupDeductorBinDStudio.exe" "C:ПутьКФайлу1c.ded" /DisableFloatExceptions=True /RUN /LOG /LOGFILE="C:ПутьКФайлуlog.log" /LOGMODE
Важно: Необходимо обратить внимание, что при изменении значения переменной DisableFloatExceptions результат работы узла Калькулятор может поменяться, а именно меняется результат обработки значения NULL.
Пример: при DisableFloatExceptions = True
значение выражения Round(Null()) > 1 возвращает False
. При DisableFloatExceptions = False
это выражение возвращает Null
.
В связи с этим, при применении данного решения необходимо проверить работу сценария.
Здравствуйте!
Часто начала появляться ошибка «invalid floating point operation», обычно закрывается нажатием кнопки «ОК». Ошибка появляется при заполнении/редактировании сметы.
Не могу понять причину появления ошибки и её связь с какими либо действиями. Иногда совсем не появляется. Иногда появляется один раз и закрывается нажатием кнопки «ОК». А иногда появляется окно с ошибкой, при нажатии на кнопку «ОК» ошибка закрывается и тут же появляется это же окно с ошибкой, далее программу приходится закрывать из диспетчера задач.
А иногда, после нескольких появлений вышеуказанной ошибки, РИК самостоятельно закрывается без каких либо сообщений.
Подозреваю что, что-то не так с системой, так как на другом компе проблем не возникает.
Система Win10, ошибка начала появляться после последнего крупного обновления винды за май 2020.
Если кто знает решение данной проблемы, будьте добры, помогите пожалуйста… усталлл
I’m getting a (repeatable) floating point exception when i try to Trunc()
a Real
value.
e.g.:
Trunc(1470724508.0318);
In reality the actual code is more complex:
ns: Real;
v: Int64;
ns := ((HighPerformanceTickCount*1.0)/g_HighResolutionTimerFrequency) * 1000000000;
v := Trunc(ns);
But in the end it still boils down to:
Trunc(ARealValue);
Now, i cannot repeat it anywhere else — just at this one spot. Where it fails every time.
It’s not voodoo
Fortunately computers are not magic. The Intel CPU performs very specific observable actions. So i should be able to figure out why the floating point operation fails.
Going into the CPU window
v := Trunc(ns)
fld qword ptr [ebp-$10]
This loads the 8-byte floating point value at ebp-$10 into floating point register ST0
.
The bytes at memory address [ebp-$10] are:
0018E9D0: 6702098C 41D5EA5E (as DWords)
0018E9D0: 41D5EA5E6702098C (as QWords)
0018E9D0: 1470724508.0318 (as Doubles)
The call succeeds, and the floating point register the contains the appropriate value:
Next is the actual call to the RTL Trunc function:
call @TRUNC
Next is the guts of Delphi RTL’s Trunc function:
@TRUNC:
sub esp,$0c wait fstcw word ptr [esp] //Store Floating-Point Control Word on the stack wait fldcw word ptr [cwChop] //Load Floating-Point Control Word fistp qword ptr [esp+$04] //Converts value in ST0 to signed integer //stores the result in the destination operand //and pops the stack (increments the stack pointer) wait fldcw word ptr [esp] //Load Floating-Point Control Word pop ecx pop eax pop edx ret
Or i suppose i could have just pasted it from the rtl, rather than transcribing it from the CPU window:
const cwChop : Word = $1F32;
procedure _TRUNC;
asm
{ -> FST(0) Extended argument }
{ <- EDX:EAX Result }
SUB ESP,12
FSTCW [ESP] //Store foating-control word in ESP
FWAIT
FLDCW cwChop //Load new control word $1F32
FISTP qword ptr [ESP+4] //Convert ST0 to int, store in ESP+4, and pop the stack
FWAIT
FLDCW [ESP] //restore the FPCW
POP ECX
POP EAX
POP EDX
end;
The exception happens during the actual fistp operation.
fistp qword ptr [esp+$04]
At the moment of this call, the ST0 register will contains the same floating point value:
Note: The careful observer will note the value in the above screenshot doesn’t match the first screenshot. That’s because i took it on a different run. I’d rather not have to carefully redo all the constants in the question just to make them consistent — but trust me: it’s the same when i reach the
fistp
instruction as it was after thefld
instruction.
Leading up to it:
sub esp,$0c
: I watch it push the the stack down by 12 bytesfstcw word ptr [esp]
: i watch it push $027F into the the current stack pointerfldcw word ptr [cwChop]
: i watch the floating point control flags changefistp qword ptr [esp+$04]
: and it’s about to write the Int64 into the room it made on the stack
and then it crashes.
What can actually be going on here?
It happens with other values as well, it’s not like there’s something wrong with this particular floating point value. But i even tried to setup the test-case elsewhere.
Knowing that the 8-byte hex value of the float is: $41D5EA5E6702098C
, i tried to contrive the setup:
var
ns: Real;
nsOverlay: Int64 absolute ns;
v: Int64;
begin
nsOverlay := $41d62866a2f270dc;
v := Trunc(ns);
end;
Which gives:
nsOverlay := $41d62866a2f270dc;
mov [ebp-$08],$a2f270dc mov [ebp-$04],$41d62866
v := Trunc(ns)
fld qword ptr [ebp-$08] call @TRUNC
And at the point of the call
to @trunc
, the floating point register ST0 contains a value:
But the call does not fail. It only fails, every time in this one section of my code.
What could be possibly happening that is causing the CPU to throw an invalid floating point exception
?
What is the value of cwChop
before it loads the control word?
The value of cwChop
looks to be correct before the load control word, $1F32
. But after the load, the actual control word is wrong:
Bonus Chatter
The actual function that is failing is something to convert high-performance tick counts into nanoseconds:
function PerformanceTicksToNs(const HighPerformanceTickCount: Int64): Int64;
//Convert high-performance ticks into nanoseconds
var
ns: Real;
v: Int64;
begin
Result := 0;
if HighPerformanceTickCount = 0 then
Exit;
if g_HighResolutionTimerFrequency = 0 then
Exit;
ns := ((HighPerformanceTickCount*1.0)/g_HighResolutionTimerFrequency) * 1000000000;
v := Trunc(ns);
Result := v;
end;
I created all the intermeidate temporary variables to try to track down where the failure is.
I even tried to use that as a template to try to reproduce it:
var
i1, i2: Int64;
ns: Real;
v: Int64;
vOver: Int64 absolute ns;
begin
i1 := 5060170;
i2 := 3429541;
ns := ((i1*1.0)/i2) * 1000000000;
//vOver := $41d62866a2f270dc;
v := Trunc(ns);
But it works fine. There’s something about when it’s called during a DUnit unit test.
Floating Point control word flags
Delphi’s standard control word: $1332
:
$1332 = 0001 00 11 00 110010
0 ;Don't allow invalid numbers
1 ;Allow denormals (very small numbers)
0 ;Don't allow divide by zero
0 ;Don't allow overflow
1 ;Allow underflow
1 ;Allow inexact precision
0 ;reserved exception mask
0 ;reserved
11 ;Precision Control - 11B (Double Extended Precision - 64 bits)
00 ;Rounding control -
0 ;Infinity control - 0 (not used)
The Windows API required value: $027F
$027F = 0000 00 10 01 111111
1 ;Allow invalid numbers
1 ;Allow denormals (very small numbers)
1 ;Allow divide by zero
1 ;Allow overflow
1 ;Allow underflow
1 ;Allow inexact precision
1 ;reserved exception mask
0 ;reserved
10 ;Precision Control - 10B (double precision)
00 ;Rounding control
0 ;Infinity control - 0 (not used)
The crChop
control word: $1F32
$1F32 = 0001 11 11 00 110010
0 ;Don't allow invalid numbers
1 ;Allow denormals (very small numbers)
0 ;Don't allow divide by zero
0 ;Don't allow overflow
1 ;Allow underflow
1 ;Allow inexact precision
0 ;reserved exception mask
0 ;unused
11 ;Precision Control - 11B (Double Extended Precision - 64 bits)
11 ;Rounding Control
1 ;Infinity control - 1 (not used)
000 ;unused
The CTRL
flags after loading $1F32
: $1F72
$1F72 = 0001 11 11 01 110010
0 ;Don't allow invalid numbers
1 ;Allow denormals (very small numbers)
0 ;Don't allow divide by zero
0 ;Don't allow overflow
1 ;Allow underflow
1 ;Allow inexact precision
1 ;reserved exception mask
0 ;unused
11 ;Precision Control - 11B (Double Extended Precision - 64 bits)
11 ;Rounding control
1 ;Infinity control - 1 (not used)
00011 ;unused
All the CPU is doing is turning on a reserved, unused, mask bit.
RaiseLastFloatingPointError()
If you’re going to develop programs for Windows, you really need to accept the fact that floating point exceptions should be masked by the CPU, meaning you have to watch for them yourself. Like Win32Check
or RaiseLastWin32Error
, we’d like a RaiseLastFPError
. The best i can come up with is:
procedure RaiseLastFPError();
var
statWord: Word;
const
ERROR_InvalidOperation = $01;
// ERROR_Denormalized = $02;
ERROR_ZeroDivide = $04;
ERROR_Overflow = $08;
// ERROR_Underflow = $10;
// ERROR_InexactResult = $20;
begin
{
Excellent reference of all the floating point instructions.
(Intel's architecture manuals have no organization whatsoever)
http://www.plantation-productions.com/Webster/www.artofasm.com/Linux/HTML/RealArithmetica2.html
Bits 0:5 are exception flags (Mask = $2F)
0: Invalid Operation
1: Denormalized - CPU handles correctly without a problem. Do not throw
2: Zero Divide
3: Overflow
4: Underflow - CPU handles as you'd expect. Do not throw.
5: Precision - Extraordinarily common. CPU does what you'd want. Do not throw
}
asm
fwait //Wait for pending operations
FSTSW statWord //Store floating point flags in AX.
//Waits for pending operations. (Use FNSTSW AX to not wait.)
fclex //clear all exception bits the stack fault bit,
//and the busy flag in the FPU status register
end;
if (statWord and $0D) <> 0 then
begin
//if (statWord and ERROR_InexactResult) <> 0 then raise EInexactResult.Create(SInexactResult)
//else if (statWord and ERROR_Underflow) <> 0 then raise EUnderflow.Create(SUnderflow)}
if (statWord and ERROR_Overflow) <> 0 then raise EOverflow.Create(SOverflow)
else if (statWord and ERROR_ZeroDivide) <> 0 then raise EZeroDivide.Create(SZeroDivide)
//else if (statWord and ERROR_Denormalized) <> 0 then raise EUnderflow.Create(SUnderflow)
else if (statWord and ERROR_InvalidOperation) <> 0 then raise EInvalidOp.Create(SInvalidOp);
end;
end;
A reproducible case!
I found a case, when Delphi’s default floating point control word, that was the cause of an invalid floating point exception (although I never saw it before now because it was masked). Now that i’m seeing it, why is it happening! And it’s reproducible:
procedure TForm1.Button1Click(Sender: TObject);
var
d: Real;
dover: Int64 absolute d;
begin
d := 1.35715152325557E020;
// dOver := $441d6db44ff62b68; //1.35715152325557E020
d := Round(d); //<--floating point exception
Self.Caption := FloatToStr(d);
end;
You can see that the ST0
register contains a valid floating point value. The floating point control word is $1372
. There floating point exception flag are all clear:
And then, as soon as it executes, it’s an invalid operation:
IE
(Invalid operation) flag is setES
(Exception) flag is set
I was tempted to ask this as another question, but it would be the exact same question — except this time calling Round()
.
Сообщение от Puporev
Код Delphi
1
2
b1:=Sqrt(DfX1); нахожу квадратный корень
b2:=Sqrt(DfX2);
Здесь при определенных значениях х, под корнем получается отрицательное число.
Если числа вводятся безконтрольно, то в этом случае можно выдавать предупреждение
Код Delphi
1
2
if DfX1<0 then showmessage(‘Подкоренное выражение меньше ноля’) else b1:=Sqrt(DfX1);
Да вы правы числа вводятся безконтрольно, вот вся программа
Delphi | ||
|
т.е мне ненужно выводить сообщение о том что число под корнем отрицательное, оно должно считаться и сравниваться с Эпсилонтом в DfX2 значение все равно отрицательное и как его посчитать не знаю.
А решаю задачу многомерной оптимизации состоящую из 7 шагов
1- ввожу X1,X2;
2- нахожу их значения в функции F(X1,X2) у меня
Delphi | ||
|
A1:=Fvek+Fvek2; получаю значение функции
3-нахожу производную для X1,X2
Delphi | ||
|
4-нахожу новый X1,X2 подставл производную в формулу
X1:=X1-L*DfX1;
X2:=X2-L*DfX2;
5-нахожу их значения в функции F(X1,X2) уже с новыми X1,X2
Delphi | ||
|
6-проверяю A1 и A2 если A1 >A2 тогда идем дальше если нет то цикл должен выполниться еще раз и уменьшить шаг т.е L:=L/2 это я в коде еще не реализовал
7—нахожу новые производные для X1,X2
Delphi | ||
|
и пытаюсь впихнуть их в корень
Delphi | ||
|
проверяю условием
sum > Eps то выполняем еще иначе получаем долгожданный ответ.
1
02 апреля 2006 года
kot_
7.3K / / 20.01.2000
Цитата:
Originally posted by Пётр …ович
Код — это, конечно, хорошо. С кодом, оно, конечно, проще. Только код разбросан по файлам.
Ошибку выкидывало тут:
Код:
double TEcology::GetFunctionSignificance (int i)
{
return (FunctionSignificance );
}
Сейчас немного поправил код (методом Н.Тыка). Пока ошибка не выскакивает. Поглядим на процесс обкатки…
У тебя, в твоем разбросанном коде гдето или выполняется конвертация строки в число или присвоение значения, и при этом не выполняется проверка на региональные установки. Т.е. вместо точки стоит запятая, или наоборот. Например, если региональные установки русские, этот код скорее всего сгенерит исключение:
Код:
double test = StrToFloat(«3.14»);
//примерно тоже будет
test = 3.14;
//примерно так правильно
//Если выбивают ошибки — смотри в хелпы — код пишу по памяти.
char buffer[2]
if(!GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_SDECIMAL,buffer,sizeof(buffer))){
DWORD result = GetLastError();
…
}
if(strcmp(buffer,»,»)){
double test = StrToFloat(«3,14»);
test = 3,14;
}
else if(strcmp(buffer,».»)){
…
}
З.Ы. Только без обид — для вопросов на подобные темы есть форум «Сообщества чайников», если актуально — начать стоит с него.