C++ convert vector<int> to vector<double>
Asked Answered
D

3

124

What is a good clean way to convert a std::vector<int> intVec to std::vector<double> doubleVec. Or, more generally, to convert two vectors of convertible types?

Dichasium answered 18/6, 2011 at 21:50 Comment(0)
G
206

Use std::vector's range constructor:

std::vector<int> intVec;
std::vector<double> doubleVec(intVec.begin(), intVec.end());
Glib answered 18/6, 2011 at 21:51 Comment(7)
So, you could also use the std::copy(...) function then? Could you add that to the answer?Dichasium
@Lex: copy(v_int.begin(), v_int.end(), back_inserter(v_float));, or v_float.resize(v_int.size()); copy(v_int.begin(), v_int.end(), v_float.begin());Undershoot
bad idea, because the constructor version will presize the vector by using the iterator category to note that those are random access iterators and then reserving enough space. Resizing prior to copy is a wasteful zero initialization.Robotize
@MichaelGoldshteyn I don't understand - if you don't specify the size beforehand, then it will be resized automatically whenever the capacity is exceeded (which copies all the elements over and over again). Okay, this is amortized linear time, but I bet that's still a lot slower than a single 0-initialization. Am I missing something about your argument?Toadinthehole
@Algoman: I agree. But yet better is reserve. Then you do one allocation and don't uselessly initialize the data to 0. The constructor version in the answer also allocates only once, since it knows how many elements will be inserted.Hidie
I think I misunderstood the answer. However I'm wondering if the c'tor version can really deduce how many elements there are directly. I mean if it only has 2 iterators and doesn't know that they are vector-iterators (does it?), I think it must iterate from begin to end to count the elements first. I think this might still be a little faster than 0-initialization, but I'm not 100% sure. If we account for the time it takes to read, compare and write the iterators and the counting-variable, it might be slower. 0-init has 1 indirection more, but c'tor would then have 1 load and 1 inc more.Toadinthehole
@Toadinthehole see std::distance() "If it is a random-access iterator, the function uses operator- to calculate this. Otherwise, the function uses the increase operator (operator++) repeatedly." So in this case the amount of elements could be found by subtracting the iterators. Whether the standard library is implemented to use std::distance for an initial reserve() is another question, but godbolt.org/z/6mcUFh at least contains a call to std::distance().Amarillis
M
11

Use std::transform algorithm:

std::transform(intVec.begin(), intVec.end(), doubleVec.begin(), [](int x) { return (double)x;});
Myrna answered 22/11, 2020 at 3:33 Comment(5)
good solution but I believe it is safer to use static_cast<double>() over (double) as it would check if casting is possible in compile timePoree
Are there any differences between this answer and the accepted answer in terms of performance or memory usage?Maganmagana
@OrHirshfeld: Better yet is no cast at all: [](int x) -> double { return x; }Brachiate
@BenVoigt I would say, this solution is the worst, because the cast is hidden.Hemimorphite
@tommsch: It's not a hidden cast. It will not do any of the dangerous things that a cast can do. It is just an implicit type conversion.Brachiate
B
-1

Since c++20, u have std::span, which is simply composed of a pointer and size, and can be used to look at a contiguous block of memory without making a copy, but with all the conveniences of a stl containers (methods, iterator, etc).

std::vector<int> int_vec = { 1, 2, 3, 4 };

constexpr size_t DOUBLE_INT_SIZE_RATIO = sizeof(double) / sizeof(int);
std::span<double> dbl_span(
    reinterpret_cast<double*>(int_vec.data()),
    int_vec.size() / DOUBLE_INT_SIZE_RATIO 
);

for (const double dbl : dbl_span) {
  // 
}

NOTE: A span does not take ownership of the memory it points to, so if the vector is destroyed or moved from, or if the internal buffer is reallocated, your span will point to invalid memory.

Bentwood answered 20/12, 2023 at 19:19 Comment(6)
This is just a (wrong, violates strict aliasing) view of the same memory, without performing the conversion. Question does mention conversion even if not directly.Brachiate
Doesn’t this simply reinterpret the original vector’s bits instead of converting them from integer to double format? If so, then the values read out of dbl_span would probably not be what the questioner wanted.Cackle
@BenVoigt My bad, I misunderstood the question. If conversion is necessary then obviously this would not work. The use case for me was with audio processing, when dealing w/ 16bit audio for example. I received a vector<uint8> from a file/network and wanted to look at it as a sequence of 16bit samples. I didn't know about the strict aliasing rule. How would u tackle the problem I described?Bentwood
@OmerMarom: This is one of the reasons I/O APIs use caller-supplied buffers, instead of allocating and returning std::vector<std::byte>. Pass uint16_t * in, and strict aliasing allows the I/O library to use std::byte when writing to it and the program to process 16-bit samples.Brachiate
@BenVoigt Don't mean to take up more of ur time but one last question: What if I'm receiving data over udp and each packet is composed of a header and audio. It makes sense to read the header as a sequence of bytes and the audio as a sequence of 16bit samples. Seeing as udp is message oriented, I can't grab parts of the packet w different buffer types as the packet is discarded at the first recv. Thoughts?Bentwood
@OmerMarom: If you want to avoid a copy, there's scatter/gather IOBrachiate

© 2022 - 2024 — McMap. All rights reserved.