How to handle floating point overflow? - delphi

I'm trying to do filtering .wav sample data value using the HAAR formula but got error "floating point overflow"
Edited : add more code
numsamples := round(wavehdr.SampleRate);
SetLength(wavedata[0].Data, numsamples);
Stream.Read(wavedata[0].Data[0], numsamples);
SetLength(cA1, numsamples);
SetLength(cD1, numsamples);
for i:=1 to numsamples-1 do begin
cA1[i]:=(wavedata[0].Data[(i*2)-1]*0.7071) + (wavedata[0].Data[(i*2)]*0.7071);
cD1[i]:=(wavedata[0].Data[(i*2)-1]*0.7071) + (wavedata[0].Data[(i*2)]*-0.7071);
end;
where wavedata[0].Data[i], i get it from function Stream.Read to load sample data value of .wav file. I don't know why i got the error or what the error means and i've been searching the error mostly caused of divizion by zero, but there is no divizion by zero in my code. So maybe i could some help here what is the error mean in my code?
EDIT 1: (i'm really new to delphi, this code is not mine i found it internet. In my understanding the following code is the one to read .wav file sample data value)
type
TWaveHeader = packed record
Marker_RIFF: array [0..3] of char;
ChunkSize: cardinal;
Marker_WAVE: array [0..3] of char;
Marker_fmt: array [0..3] of char;
SubChunkSize: cardinal;
FormatTag: word;
{ nChannels : 1 mono, 2 stereo }
NumChannels: word;
SampleRate: longint;
BytesPerSecond: longint;
BytesPerSample: word;
BitsPerSample: word;
Marker_data: array [0..3] of char;
DataBytes: longint;
end;
TChannel = record
Data : array of double;
end;
And a private declaration :
private
wavehdr:TWaveHeader;
the function :
FillChar(wavehdr, sizeof(wavehdr),0);
Stream.Read(wavehdr,sizeof(wavehdr));
i modified a bit of the code to handle null value while reading the sample data :
if(IsNan(wavedata[0].Data[(i*2)-1])) then begin
wavedata[0].Data[(i*2)-1]:=0;
end
else if(IsNan(wavedata[0].Data[(i*2)])) then begin
wavedata[0].Data[(i*2)]:=0;
end;

for i:=0 ...
(wavedata[0].Data[(i*2)-1]
Do you really have array element Data[-1] ?
P.S. Set range check compiler option while debugging.
Edit: I see some new code, so let's go with step 2:
SetLength(wavedata[0].Data, **numsamples**);
for i:=1 to **numsamples**-1
wavedata[0].Data[(**i*2)**]
Have we to check thoroughly every line of code?

Overflow occurs when an expression yields a value that does not fit in the range of the data type in which the expression is evaluated. Overflow can occur for positive and negative numbers.
Your particular expression will only result in overflow if the input values are already close to overflowing a floating point value. So, if you are using double precision values for example, then your code can only overflow if the input data has magnitude of around 1e308.
It seems unlikely that your input data is really of that form. So my guess is that your problem is related to how you read your input data. I suspect you are reading it incorrectly and so end up performing arithmetic on meaningless values.

After i try this and that i figured out my own mistakes, thanks to #MBo your answer narrowing mw focus in the loop, it's really stupid of me. The looping should be like
for i:=0 to round(numsamples/2) do begin
it is not element Data[-1] that the problem but, if length of array wavedata = X, then i try to reach the element of [X*2] which are not available it's surely after a half will cause an error. eg array[4] but i try to reach array[4*2] which is not available (Sorry for my bad english, i dont know if my explanation was good or not) but thanks everyone for the help :D

Related

How to Extract real part & Imaginary part from a String entry in the Pascal TextEdit GUI?

I am trying to create a Complex Number Calculator using Delphi Pascal. The first part is to extract a string entry, separate the real & imaginary part by inserting a delimiter on 'i'. For Example: A number entry can be: 7+2i or any other combination. This was my approach:
TForm2.btnExtractClick(Sender: TObject);
var sCode, pic: string;
sConst: integer;
im,re: integer;
iConst: string;
j,k, delimiterPos: integer;
begin
memDisplay.Clear;
sCode := Edit1.Text;
sConst := Pos (sCode, 'i');
im := StrToInt(Copy(sCode, sConst - 1));
Delete(sCode, sConstant - 1, im);
re := StrToInt(sCode);
But I am getting the following error:
The substring which you search for should be the first argument to Pos.
Replace
Pos(sCode, 'i')
with
Pos('i', sCode)
This function is documented here: http://docwiki.embarcadero.com/Libraries/en/System.Pos
There are many further issues with your code, but this answers the primary question posed here, namely why Pos is returning 0. I won't attempt to debug the rest of your code, not least because this isn't your real code because it contains compile errors.
To resolve the subsequent issues in your code you must learn to use the debugger. Step through the code and inspect the value of each of the local variables after each line of code has executed. Compare the values you see under the debugger with the values that you expect to see based on your paper based static analysis.

How to find out char code for a character of an Ansistring

In older versions of Delphi, like D7, you could do like ord(s[i]) where s was a string, but trying this with an AnsiString results in an exception (access violation).
P.S. I was w/delphi 7 for a long time.
Here are the steps to reproduce the error:
Create a new project and through a memo on the form (let it be memo1) than add the following code to the form create event handler:
procedure TForm1.FormCreate(Sender: TObject);
var u: ansistring;
begin
u := 'stringtest';
memo1.Lines.Add(inttostr(ord(u[2])));
end;
For me this code produces an AV.
It does work with an ansistring, but you cannot read past the end of it and you must make sure the string is initialized.
function CharCode(const S: ansistring; pos: integer): byte;
begin
if pos <= 0 then result:= 0
//else if s='' then Result:= 0 //unassigned string;
else if Length(s) < Pos then Result:= 0 //cannot read past the end.
else Result:= Ord(s[pos]);
end;
Note that if s='' is the same as asking if pointer(s) = nil. An empty string is really a nil pointer.
This is probably why you where getting an access violation.
If you want to force the ansistring to be a certain length you can use SetLength(MyAnsistring, NewLength);
The length of the (ansi)string is variable. That means it grows and shrinks as needed. If you read past the end of the string you may get an access violation.
Note that you don't have to get an AV, the RTL leaves a bit of slack in its allocation; it usually allocates a slightly bigger buffer than requested, this is due to performance and architectural reasons.
The other reason why you may not get an AV if reading past the end of a string is that your program may own both the string buffer and whatever happens to be right next to it.
For this reason it is a good idea to enable range checking in debug mode {$R+} it adds extra checks to protect against reading past the end of structures.
The difference between shortstring and (ansi)string
A short string has a fixed length and it lives on the stack.
A long string (ansi or wide) is a pointer to a record that gets allocated on the heap; it looks like this:
type
TStringRecord = record
CodePage: word;
ElementSize: word; //(1, 2 or 4)
ReferenceCount: integer;
Length: Integer;
StringData: array[1..length(s)] of char;
NullChar: char;
end;
The compiler hides all these details from you.
see: http://docwiki.embarcadero.com/RADStudio/Seattle/en/Internal_Data_Formats

Getting the pointer data from function parameters (Delphi)

Continuing from the topic here, being unfamiliar with pointers,
how to correctly call the function bellow, given the fact that the pointers in it are the data to be retrieved back, and how to actually get to that data after calling it?
function RetrieveDSOData(whatchannels: Byte; DSOCH1, DSOCH2: PDouble; LADATA: PWord; Nth_Sample: Byte): longint; cdecl; external 'E_l80.dll';
(Needed DSOCH1, DSOCH2 and LADATA data...)
If it's in any help / important at all -> the documentation states that these are "pointers to an array".
In case this is a duplicate and I just didn't look by correct keywords, vote for closing this.
Thank you.
Edit:
It is to be assumed the size can be received by the return of the function. Documentation states:
Return: The number of samples in the DSO and LA arrays. This number may not
be (total blocks x 1024) as some samples at the beginning and end are thrown away for
various purposes.
So, max size might be (1024 x 32samples x 2 (2x8 digital channels, 2 bytes?)) for LADATA array...
You have to call the function with addresses of arrays, and it will fill these arrays. Example:
var
DS1: array[0..31] of Double;
RetrieveDSOData(whatchannels, #DS[0], ...

Delphi XE3: Convert WideChar array to Unicode string HOWTO?

I've done some research here regarding the problem given above and come up with the following code:
VarStr = array of WideChar;
function ArrayToString(const a: VarStr): UnicodeString;
begin
if Length(a) > 0 then
begin
ShowMessage ('Länge des übergebenen Strings: ' + IntToStr(Length(a)));
SetString(Result, PWideChar(#a[0]), Length(a) div 2)
end
else
Result := '';
end;
ShowMessage displays the correct number of characters in a given array, but the result of the function is always an empty string.
Your ideas please?
You are passing the wrong length value. You only ask for half of the characters. Fix your code like this:
function ArrayToString(const a: VarStr): string;
begin
SetString(Result, PWideChar(a), Length(a));
end;
However, you also report that your function returns an empty string. The most likely cause for that is that you are passing invalid input to the function. Consider this program:
{$APPTYPE CONSOLE}
type
VarStr = array of WideChar;
function ArrayToStringBroken(const a: VarStr): UnicodeString;
begin
SetString(Result, PWideChar(#a[0]), Length(a) div 2);
end;
function ArrayToStringSetString(const a: VarStr): UnicodeString;
begin
SetString(Result, PWideChar(a), Length(a));
end;
var
a: VarStr;
begin
a := VarStr.Create('a', 'b', 'c', 'd');
Writeln(ArrayToStringBroken(a));
Writeln(ArrayToStringSetString(a));
end.
The output is:
ab
abcd
So as well as the problem with the code in your question, you would seem to have problems with the code that is not in your question.
Perhaps when you said:
The result of the function is always an empty string.
You actually meant that no text is displayed when you pass the returned value to ShowMessage. That's a completely different thing altogether. As #bummi points out in comments, ShowMessage will truncate its input at the first null-terminator that is encountered. Use proper debugging tools to inspect the contents of variables.
Result:= Trim(string(a));
UPDATE: As colleagues graciously pointed in comments, this is a wrong answer! It works only because internal string and dynamic array implementation are pretty similar and there is no guarantee that such code would work in the future compilator versions. The correct way to DynArray->String conversion is described in the David answer. I would not delete my answer to preserve comments, in my opinion their worth is much greater..

Assign String to Array of Characters

Question One
I have
var example : array[0..15] of char;
I want to assign the value from an input to that variable
example := inputbox('Enter Name', 'Name', '');
In the highscores unit I have record and array
type
points = record
var
_MemoryName : array[0..15] of char;
_MemoryScore : integer;
end;
var
rank : array[1..3] of points;
var s: string;
a: packed array[0..15] of char;
highscoresdata.position[1]._MemoryName := StrPLCopy(a, s, Length(a)) ;
returns -> (186): E2010 Incompatible types: 'array[0..15] of Char' and 'PWideChar'
var s: string;
a: packed array[0..15] of char;
s := InputBox('caption', 'Caption', 'Caption');
FillChar(a[0], length(a) * sizeof(char), #0);
Move(s[1], a[0], length(a) * sizeof(char));
scores.rank[1]._MemoryName := <<tried both s and a>> ;
returns (189): E2008 Incompatible types
Question One
There are many ways. One is:
procedure TForm1.FormCreate(Sender: TObject);
var
s: string;
a: packed array[0..15] of char;
begin
s := InputBox(Caption, Caption, Caption);
assert(length(s) <= 16);
FillChar(a[0], length(a) * sizeof(char), #0);
Move(s[1], a[0], length(s) * sizeof(char));
end;
But there might be a more elegant solution to your original problem, I suspect.
Question Two
Every time you wish a function/procedure didn't have a particular argument, you should realize that there might be a problem with the design of the project. Nevertheless, it isn't uncommon that Sender parameters are superfluous, because they are almost omnipresent because of the design of the VCL (in particular, the TNotifyEvent). If you know that the receiving procedure doesn't care about the Sender parameter, simply give it anything, like Self or nil.
Question Three
Consider this code:
procedure TForm4.FormCreate(Sender: TObject);
var
a: packed array[0..15] of char;
b: packed array[0..15] of char;
begin
a := b;
end;
This doesn't work. You cannot treat arrays like strings; in particular, you cannot assign static arrays like this (a := b).
Instead, you have to do something like...
Move(b[0], a[0], length(a) * sizeof(char));
...or simply loop and copy one value at a time. But the above simple assignment (a := b) does work if you declare a static array type:
type
TChrArr = packed array[0..15] of char;
procedure TForm4.FormCreate(Sender: TObject);
var
a: TChrArr;
b: TChrArr;
begin
b := a;
end;
Andreas has you covered for question 1.
Question 2
I would arrange that your event handler called another method:
procedure TForm5.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
RespondToEditControlKeyPress;
end;
That way you can just call RespondToEditControlKeyPress directly.
I'd guess that you want to call it with no parameters because you want code to run when the edit control's text is modified. You could perhaps use the OnChange event instead. And it may be that OnChange is more appropriate because pressing a key is not the only way to get text into an edit control.
By the way, it's better to ask one question at a time here on Stack Overflow.
For a quick way to copy string-type values into array-of-character type values. I suggest a small helper function like this:
procedure StrToCharArray( inputStr:String; var output; maxlen:Integer);
type
ArrayChar = Array[0..1] of Char;
begin
StrLCopy( PChar(#ArrayChar(output)[0]),PChar(inputStr),maxlen);
end;
Each time you call it, pass in the maximum length to be copied. Remember that if the buffer length is 15, you should pass in 14 as the maxlen, so that you leave room for the terminating nul character, if you intend to always terminate your strings:
StrToCharArray( UserInputStr, MyRecord.MyField, 14 );
This function will ensure that the data you copy into the record is null terminated, assuming that's what you wanted. Remember that in a fixed length character array it's up to you to decide what the rules are. Null terminated? Fully padded with spaces or null characters.... Strings and arrays-of-characters are so different, that there exist multiple possible ways of converting between the two.
If you don't intend to terminate your strings with nul, then you should use the FillChar+Move combination shown in someone else's answer.
The obvious answer is of course.
Don't use a packed array of char.
Use a string instead.
If you use ansistring, 1 char will always take 1 byte.
If you use shortstring ditto.
Ansistring is compatible with Pchar which is a pointer to a packed array of char.
So you can write
function inputbox(a,b,c: ansistring): pchar;
begin
Result:= a+b+c;
end;
var s: ansistring;
begin
s:= inputbox('a','b','c');
end;
Some advice
It looks like your are translating code from c to Delphi.
a packed array of char is exactly the same as the old (1995) shortstring minus the length byte at the beginning of shortstring.
The only reason I can think of to use packed array of char is when you are reading data to and from disk, and you have legacy code that you don't want to change.
I would keep the legacy code to read and write from disk and then transfer the data into an ansistring and from there on only use ansistring.
It's soooooooo much easier, Delphi does everything for you.
And... ansistring is much faster, gets automatically created and destroyed, can have any length (up to 2GB), uses less memory --because identical strings only get stored once (which means stringa:= stringb where a string is 20 chars is at least 5x faster using ansistrings than array's of char).
And of course best of all, buffer overflow errors are impossible with ansistring.
What about unicodestring?
Unicodestring is fine to use, but sometimes translation of chars happens when converting between packed array of char and unicodestring, therefore I recommend using ansistring in this context.
What you try to do is impossible, indeed:
highscoresdata.position[1]._MemoryName := StrPLCopy(a, s, Length(a));
That tries to assign a pointer (the result of StrPLCopy, a PWideChar in the last few versions of Delphi) to an array, which is indeed impossible. You can't copy an array like that. I would do:
StrLCopy(highscoresdata.position[1]._MemoryName, PChar(s),
Length(highscoresdata.position[1]._MemoryName));
That should work, and is IMO the simplest solution to copy a string to an array of characters. There is no need to use a as some kind of intermediate, and using Move is, IMO, rather low level and therefore a little tricky (it is easy to forget to multiply by the size of a character, it is unchecked, it does not add a #0, etc.), especially if you don't know what exactly you are doing.
This solution should even work for versions of Delphi before Delphi 2009, as it does not rely on the size of the character.
FWIW, I would not use packed arrays. Packed doesn't have a meaning in current Delphi, but could confuse the compiler and make the types incompatible.

Resources